Shiro(十三)RememberMe

1,093 阅读2分钟

介绍

Shiro中的Remember Me功能可以让用户在下次访问时无需再次输入用户名和密码,而是直接通过浏览器的Cookie或其他方式自动登录。下面介绍一下Shiro Remember Me的实现原理和使用机制,以及在Spring Boot框架中的使用方法:

Remember Me实现原理

Shiro Remember Me实现的原理主要是通过Cookie实现的。当用户选择“记住我”选项时,Shiro会将用户的身份信息加密并存储到Cookie中,在下次访问时会自动读取Cookie中的信息并进行身份认证。

具体地,Shiro中的Remember Me功能主要是通过RememberMeFilter实现的。当用户访问时,RememberMeFilter会首先检查是否存在Remember Me Cookie,如果存在则自动尝试使用该Cookie中的信息进行身份认证。

Remember Me使用机制

在Shiro中启用Remember Me功能需要进行相应的配置。具体地,需要在shiro.ini或shiro-realm.ini文件中添加以下配置:

# Remember Me configuration
[main]
...
# Cookie的名称
cookie.name=rememberMe
# Cookie的过期时间,单位为秒
cookie.maxAge=2592000
# Remember Me Cookie加密的密钥
securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==
# 使用cookie加密Remember Me功能
securityManager.rememberMeManager = org.apache.shiro.web.mgt.CookieRememberMeManager
# 记住我登录页面的地址
user.loginUrl=/user/login

以上配置可以实现基本的Remember Me功能,并对Cookie的过期时间和加密密钥进行相应的设置。

在Spring Boot框架中的配置

在Spring Boot框架中使用Shiro Remember Me功能,只需要添加Shiro和Spring Boot的依赖,并在application.yml或application.properties文件中配置相关参数即可。例如,以下是application.yml中相关配置的示例:

shiro:
  cookie:
    name: rememberMe
    maxAge: 2592000
  securityManager:
    rememberMeManager:
      cipherKey: kPH+bIxk5D2deZiIxcaaaA==
  user:
    loginUrl: /user/login

在以上配置中,使用了相同的配置参数,并使用Spring Boot默认的yml格式进行配置。在应用程序中,可以通过inject方式注入相应的组件来使用Remember Me功能:

在pom.xml文件中添加Shiro和Apache Shiro的Web支持依赖:

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.7.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>1.7.1</version>
    </dependency>

在Spring Boot的配置文件application.yml中配置Shiro:

    shiro:
      loginUrl: /login
      successUrl: /index
      unauthorUrl: /unauthorized
      cookie:
        name: rememberMe
        maxAge: 604800
        cipherKey: 3AvVhmFLUs0KTA3Kprsdag==

在Spring Boot的配置类中添加Shiro的配置:


    @Configuration
    public class ShiroConfig {

        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
            filterFactoryBean.setSecurityManager(securityManager);

            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/logout", "logout");
            filterChainDefinitionMap.put("/**", "user");

            filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            filterFactoryBean.setLoginUrl("/login");
            filterFactoryBean.setSuccessUrl("/index");
            filterFactoryBean.setUnauthorizedUrl("/unauthorized");

            return filterFactoryBean;
        }

        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm());
            securityManager.setRememberMeManager(rememberMeManager());
            return securityManager;
        }

        @Bean
        public MyRealm myRealm() {
            return new MyRealm();
        }

      // Remember Me组件
      @Bean
      public RememberMeManager getRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        // 设置Cookie的过期时间,单位为秒
        cookieRememberMeManager.setCookieMaxAge(2592000);
        // 设置Remember Me Cookie加密的密钥
        cookieRememberMeManager.setCipherKey(Base64.decode("kPH+bIxk5D2deZiIxcaaaA=="));
        return cookieRememberMeManager;
      }

        @Bean
        public SimpleCookie rememberMeCookie() {
            SimpleCookie cookie = new SimpleCookie("rememberMe");
            cookie.setMaxAge(604800);
            return cookie;
        }
    }

在自定义的Realm类中添加rememberMe的支持:

    public class MyRealm extends AuthorizingRealm {

        @Autowired
        private UserService userService;

        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }

        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

            User user = userService.selectByUsername(token.getUsername());
            if (user == null) {
                throw new UnknownAccountException();
            }

            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        }

        @Override
        public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
            matcher.setHashAlgorithmName("MD5");
            matcher.setHashIterations(2);
            super.setCredentialsMatcher(matcher);
        }

        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof UsernamePasswordToken || token instanceof RememberMeAuthenticationToken;
        }
    }

在登录页面的表单中添加rememberMe的选项:

    <form class="form-signin" method="post" action="/login">
        <h2 class="form-signin-heading">Please sign in</h2>
        <input type="text" name="username" class="form-control" placeholder="Username" required autofocus>
        <input type="password" name="password" class="form-control" placeholder="Password" required>
        <label class="checkbox">
            <input type="checkbox" name="rememberMe" value="true"> Remember me
        </label>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </form>

在以上代码中,我们创建了RememberMeManager组件,并将其注入到SecurityManager中,在ShiroFilterFactoryBean中设置了Remember Me的相关过滤器,以实现Remember Me功能。