介绍
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功能。