shiro安全控制目录
一般在登录网站上含有这么一个按钮(X天自动登录),在一次验证成功之后。用户后续X天内在同一个浏览器继续访问该页面时,不用经过认证,便可访问数据。
代码配置
SecurityManager作为Shiro核心,协调管理着rememberManager组件进行安全控制,认证流程如图1所示:
1.1 securityManager配置
在SecurityManager中配置rememberManager,如代码1所示。
代码1:Spring中rememberManager的配置
1.2 业务代码
设置了token.setRememberMe(true);
属性,如代码2所示。
代码2:业务代码中开启remember功能
@RequestMapping("/login")
public String login(@Param("username") String username,@Param("password") String password) {
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername(username);
token.setPassword(password.toCharArray());
//开启rememberMe功能
token.setRememberMe(true);
//调用验证
Subject subject = SecurityUtils.getSubject();
subject.login(token);
}
1.3 shiro Filter配置
shiro觉得不能把rememberMe等同于完全认证,这样是很不安全的,即若用户的cookie中rememberMe信息泄露,那么可能会造成安全漏洞。所有针对于rememberMe这个功能,shiro提供了两种Filter级别,代码3所示。
代码3:Shiro Filter的两种配置
//只有经过Realm认证后才不会被拦截
/** = authc
//Realm认证或开启RememberMe均不会被拦截
/** = user
文章参考:shiro(7)-shiroFilter(url层次认证/权限控制)
若想使用rememberMe功能,shiro Filter必须设置为/** = user。
经过认证后,请求中的cookie信息,如图2所示:
源码分析
2.1 将rememberMe保存到cookie中
在rememberManager中对principals进行加密(这个值就是自定义Realm返回的SimpleAuthenticationInfo
对象的属性)
源码:org.apache.shiro.mgt.AbstractRememberMeManager#rememberIdentity
protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
byte[] bytes = serialize(principals);
if (getCipherService() != null) {
//配置文件传入的私钥和principals进行加密
bytes = encrypt(bytes);
}
return bytes;
}
将加密串设置到cookie中
源码:org.apache.shiro.web.mgt.CookieRememberMeManager#rememberSerializedIdentity
protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {
//省略部分代码
//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中的rememberMe参数
cookie.setValue(base64);
cookie.saveTo(request, response);
}
2.2 验证cookie中的rememberMe字段
获取cookie的字段,构建subject对象
请求上送时,在Shiro Filter中创建Subject对象,创建Subject对象后,将其加入到ThreadLocal中,而后再获取subject,只需要在线程上下文获取即可。
源码:org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
//创建Subject对象(此时,已经获取到rememberMe上送的原始的principal对象)
final Subject subject = createSubject(request, response);
}
//省略部分源码
}
在createSubject(request, response)方法实现中,实际上获取cookie中上送的rememberMe字段(源码参看org.apache.shiro.web.servlet.SimpleCookie#readValue
),然后将其解密获取到原始principal对象。
源码:org.apache.shiro.mgt.AbstractRememberMeManager#getRememberedPrincipals
public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
PrincipalCollection principals = null;
try {
//在SimpleCookie中获取到rememberMe字段
byte[] bytes = getRememberedSerializedIdentity(subjectContext);
//SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
if (bytes != null && bytes.length > 0) {
//进行解密,获取到原生的principal对象
principals = convertBytesToPrincipals(bytes, subjectContext);
}
} catch (RuntimeException re) {
principals = onRememberedPrincipalFailure(re, subjectContext);
}
return principals;
}
此时之后,执行完毕createSubject
方法,将subject放入到线程的上下文中。
shiro Filter过滤请求
使用org.apache.shiro.web.filter.authc.UserFilter
过滤器,若是含有subject对象中含有principal对象,则放行。那么我们只要cookie中上送正确的rememberMe字段,便可以通过认证。
源码:org.apache.shiro.web.filter.authc.UserFilter#isAccessAllowed
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginRequest(request, response)) {
return true;
} else {
Subject subject = getSubject(request, response);
// If principal is not null, then the user is known and should be allowed access.
return subject.getPrincipal() != null;
}
}
总结
shiro开启rememberMe功能后,登录成功后,会单独返回客户端一个cookie字段(rememberMe),保存着用户信息(principals对象)。当服务器shiro Filter认证过滤器为/**=user,用户请求的cookie中携带rememberMe字段。服务器并且可以解析rememberMe的加密串,那么则允许请求访问服务器数据。
这样可能会导致安全问题。故针对一些重要的数据,shiro Filter认证过滤器可以设置为/**=authc。
文章参考
Shiro的 rememberMe 功能使用指导(为什么rememberMe设置了没作用?)