目录

  • 1、什么是rememberMe
  • 2、rememberMe工作原理
  • 3、使用rememberMe
  • 3 、使用自定义rememberMe
  • 4、总结

1、什么是rememberMe

shiro为我们提供了rememberMe功能,也就是记住我功能,这个功能的作用很简单,就是记住我,很多网站上都会有15天免登陆之类的功能,当我们设置了rememberMe功能后,只要登录一次,在不手动退出登录的情况下直接关闭浏览器,然后再次打开浏览器,仍然可以直接访问到属于自己的信息,这里就实现了记住我功能。

2、rememberMe工作原理

  • 我们打开浏览器,进入我们的权限验证页面,可以看到session信息

remember me 关闭 shiro rememberme 功能关闭_拦截器

  • 在我们勾选了记住我后,再次登陆,如果登录成功,则会在此位置生成记住我的一个cooike,shiro会记住我们这个识别码,用作登陆过用户的标记来实现user拦截器

remember me 关闭 shiro rememberme 功能关闭_免登陆_02

  • 我们查看shiro登录的部分源码,可以找到在AbstractRememberMeManager类的287行,部分判断记住我源码
public void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info) {
        //always clear any previous identity:
        forgetIdentity(subject);

        //如果记住我是true,会执行下面这段代码
        if (isRememberMe(token)) {
            rememberIdentity(subject, token, info);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("AuthenticationToken did not indicate RememberMe is requested.  " +
                        "RememberMe functionality will not be executed for corresponding account.");
            }
        }
    }

继续深入查看,可以查看到最终调用了CookieRememberMeManager类中部分代码实现了写入cookie

protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {

        if (!WebUtils.isHttp(subject)) {
            if (log.isDebugEnabled()) {
                String msg = "Subject argument is not an HTTP-aware instance.  This is required to obtain a servlet " +
                        "request and response in order to set the rememberMe cookie. Returning immediately and " +
                        "ignoring rememberMe operation.";
                log.debug(msg);
            }
            return;
        }


        HttpServletRequest request = WebUtils.getHttpRequest(subject);
        HttpServletResponse response = WebUtils.getHttpResponse(subject);

        //base 64 encode it and store as a cookie:
        String base64 = Base64.encodeToString(serialized);

        Cookie template = getCookie(); //the class attribute is really a template for the outgoing cookies
        Cookie cookie = new SimpleCookie(template);
        cookie.setValue(base64);
        cookie.saveTo(request, response);
    }

3、使用rememberMe

  1. 在登录接口上设置setRememberMe(true),我们在页面上已经设置好,勾选选项即可
Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        if (rememberMe != null) {
            token.setRememberMe(rememberMe);
        }
        try {
            // 登录
            subject.login(token);
  1. 在ShiroController新增/user接口
@RequestMapping("/user")
    public ReturnMap user() {
        return new ReturnMap().success().data("user可以访问");
    }
  1. 在shiro的 shirFilter方法中新增user拦截器,

注意要写在 “/**”, "authc"前面

@Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        // 配置退出 过滤器,其中具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/user", "user");
        // 因为目前演示页面依附在此项目下,特为演示页面新增可无权限访问,前后端分离后无需此设置
        filterChainDefinitionMap.put("/login.html", "anon");
        // <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->因为保存在LinkedHashMap中,顺序很重要
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "authc");// 设置/** 为user后,记住我才会生效
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面,前后端分离设置此为controller返回的未登录的接口
        // --------------------------------------------------
        // 前后端分离使用下面设置
        // shiroFilterFactoryBean.setLoginUrl("/login.html");
        shiroFilterFactoryBean.setLoginUrl("/unauthorized");// 前后端分离只需要把需要登录返回告诉前端页面即可
        // ---------------------------------------------------
        // 登录成功后跳转的链接,前后端分离不用设置
        // shiroFilterFactoryBean.setSuccessUrl("/index");

        // 未授权的界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }
  1. 页面新增user接口按钮

remember me 关闭 shiro rememberme 功能关闭_remember me 关闭_03

  1. 进行测试
  • 未登录状态直接点击user按钮(无法访问)

remember me 关闭 shiro rememberme 功能关闭_Springboot2集成Shiro框架_04

  • 未勾选记住我,登录成功后访问user按钮(可以访问)

remember me 关闭 shiro rememberme 功能关闭_Springboot2集成Shiro框架_05

  • 关闭浏览器后再打开,点击访问user(无权限,并且sessionid变成全新的)

remember me 关闭 shiro rememberme 功能关闭_remember me 关闭_06

  • 勾选记住我登录,可以看到user接口可以访问,同时cookie下出现了rememberMe的cookie,有效期一年,默认httponly

remember me 关闭 shiro rememberme 功能关闭_Springboot2集成Shiro框架_07

  • 重新打开浏览器,直接点击访问user接口(可以访问,在这可以看到,rememberMe一直存在,sessionid变成全新的了)

remember me 关闭 shiro rememberme 功能关闭_拦截器_08

  • 这里需要注意的是,即使你上次登录的是guest用户,选择了记住我功能,但是仍然不能使用rememberMe功能实现guest接口的访问,这也是shiro故意控制的一点

remember me 关闭 shiro rememberme 功能关闭_拦截器_09

3 、使用自定义rememberMe

  1. 在ShiroConfig类中添加cookie对象
/**
     * Cookie 对象 用户免登陆操作,但是需要配置filter /** 权限为user生效
     * 
     * @return
     */
    public SimpleCookie rememMeCookie() {
        // 初始化设置cookie的名称
        SimpleCookie simpleCookie = new SimpleCookie("boot-shiro");
        simpleCookie.setMaxAge(2592000);// 设置cookie的生效时间
        simpleCookie.setHttpOnly(true);
        return simpleCookie;
    }
  1. 添加cookie管理器bean
// remeberMe cookie 加密的密钥 各个项目不一样 默认AES算法 密钥长度(128 256 512)
    private static final String ENCRYPTION_KEY = "3AvVhmFLUs0KTA3Kprsdag==";
    /**
     * cookie 管理对象,记住我功能
     * 
     * @return
     */
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememMeCookie());
        // remeberMe cookie 加密的密钥 各个项目不一样 默认AES算法 密钥长度(128 256 512)
        cookieRememberMeManager.setCipherKey(Base64.decode(ENCRYPTION_KEY));
        return cookieRememberMeManager;
    }
  1. 把cookie管理器交给SecurityManager
/**
     * 注入 securityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setRememberMeManager(rememberMeManager());//把cookie管理器交给SecurityManager
        return securityManager;
    }

4、总结

  1. shiro的基础篇到此就结束了,后续会更新shiro使用ehcache和redis的总结以及遇到的问题的篇幅
  2. 需要源码可以点击这里获取!