在《Web安全-使用Shiro做认证和访问控制》 中,我简单了说明了shiro的用法,今天来讲下利用shiro怎么实现记住我功能,如下图:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9EFVNNzaU5UeBR0T592MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1EDOyQDN1ATM5ETNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
如果勾选了记住我,那么在关闭浏览器或服务器重启后,用户打开网站会自动登录进去,无需再次输入密码。
据我了解,记住我功能应该是shiro把用户信息序列化到cookie中,传到后台的时候再解密的。
需要增加记住我相关的配置:
/**
* rememberMe管理器, cipherKey生成见{@code Base64Test.java}
*/
@Bean
public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
CookieRememberMeManager manager = new CookieRememberMeManager();
manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
manager.setCookie(rememberMeCookie);
return manager;
}
/**
* 记住密码Cookie
*/
@Bean
public SimpleCookie rememberMeCookie() {
SimpleCookie simpleCookie = new SimpleCookie(REMEMBERME_KEY);
simpleCookie.setHttpOnly(true);
simpleCookie.setMaxAge(REMEMBERME_TIMEOUT);
return simpleCookie;
}
/**
* session管理器(单机环境)
*/
@Bean
public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setCacheManager(cacheShiroManager);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setName("shiroCookie");
cookie.setHttpOnly(true);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}
利用SecurityUtils工具类可以获取信息:
// 获取信息
return (UserPageModel) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
// 是在shiro登录鉴权的时候放入的,就是从库里查询的user对象
UserModel user = loginService.getLoginUserByMobile(token.getUsername());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
但是一般我们都是从session中获取指定的key作为是否登录的依据,但是session超时、关闭浏览器或重启服务器后就不行了,所以我们要修改相应的代码。
我们是统一了获取session的方法,所以直接在这个方法中处理就好了。其他情况可以使用拦截器解决。
public AdminSessionInfo getSessionInfo() {
Object sessionInfoObj = request.getSession().getAttribute(Constant.ADMIN_SESSION_INFO);
if (sessionInfoObj != null) {
log.debug("sessionInfo已存在sesssion中, 无需从数据库查询!");
return (AdminSessionInfo) sessionInfoObj;
}
UserPageModel shiroUser = ShiroUtil.getSessionUser();
if (shiroUser == null) {
return null;
}
// 我们session中包装了用户部门、角色等信息,所以还需要查库一次(会先走缓存)
AdminSessionInfo sessionInfo = loginService.getSessionInfoByUserId(ShiroUtil.getSessionUser());
if (sessionInfo != null) {
log.debug("首次获取sessionInfo, 需要从数据库中查询!");
ShiroUtil.setSessionAttr(Constant.ADMIN_SESSION_INFO, sessionInfo);
return sessionInfo;
}
return null;
}
记录下配置过程,以及踩的坑。
- 在shiro中,session和rememberMe的cookie是分开的,名字都是可以自定义的
- 浏览器清除缓存后,记住我功能会失效
- 注意user实体需要序列化
- 记住我subject.isRemembered为true、subject.isAuthenticated为false,登录的则相反
- rememberme登录的用户,只能访问user权限控制的路径,authc还需要登录的。比如强制下单的时候必须登录
有一些敏感性的操作,比如删除数据、下单等,可以将权限设置为authc级别,如果是记住我自动登录的,需要再次引导用户输入密码确认。
可通过覆盖shiro默认的authc级别拦截器来实现。