shiro 获取请求头中的 rememberMe
前言:
上一篇提到了, 将 sessionId 放到请求头中去, 那rememberMe是否也可以放到请求头中去呢.
其实不管是sessionId还是rememberMe, shiro都会默认往cookie里面放, 那么rememberMe肯定也是可以放到请求头中去的.
有兴趣的朋友可以去看看 org.apache.shiro.web.mgt.CookieRememberMeManager 的实现.
废话不多说了, 直接上实现吧.
一. 实现
import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.AbstractRememberMeManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.SubjectContext; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.subject.WebSubjectContext; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HeaderRememberMeManager extends AbstractRememberMeManager { private static final Logger log = LoggerFactory.getLogger(HeaderRememberMeManager.class); // header 中 固定使用的 key public static final String DEFAULT_REMEMBER_ME_HEADER_NAME = "remember-me"; @Override 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); } } else { HttpServletResponse response = WebUtils.getHttpResponse(subject); String base64 = Base64.encodeToString(serialized); // 设置 rememberMe 信息到 response header 中 response.setHeader(DEFAULT_REMEMBER_ME_HEADER_NAME, base64); } } private boolean isIdentityRemoved(WebSubjectContext subjectContext) { ServletRequest request = subjectContext.resolveServletRequest(); if (request == null) { return false; } else { Boolean removed = (Boolean) request.getAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY); return removed != null && removed; } } @Override protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) { if (!WebUtils.isHttp(subjectContext)) { if (log.isDebugEnabled()) { String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to retrieve the rememberMe cookie. Returning immediately and ignoring rememberMe operation."; log.debug(msg); } return null; } else { WebSubjectContext wsc = (WebSubjectContext) subjectContext; if (this.isIdentityRemoved(wsc)) { return null; } else { HttpServletRequest request = WebUtils.getHttpRequest(wsc); // 在request header 中获取 rememberMe信息 String base64 = request.getHeader(DEFAULT_REMEMBER_ME_HEADER_NAME); if ("deleteMe".equals(base64)) { return null; } else if (base64 != null) { base64 = this.ensurePadding(base64); if (log.isTraceEnabled()) { log.trace("Acquired Base64 encoded identity [" + base64 + "]"); } byte[] decoded = Base64.decode(base64); if (log.isTraceEnabled()) { log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes."); } return decoded; } else { return null; } } } } private String ensurePadding(String base64) { int length = base64.length(); if (length % 4 != 0) { StringBuilder sb = new StringBuilder(base64); for (int i = 0; i < length % 4; ++i) { sb.append('='); } base64 = sb.toString(); } return base64; } @Override protected void forgetIdentity(Subject subject) { if (WebUtils.isHttp(subject)) { HttpServletRequest request = WebUtils.getHttpRequest(subject); HttpServletResponse response = WebUtils.getHttpResponse(subject); this.forgetIdentity(request, response); } } @Override public void forgetIdentity(SubjectContext subjectContext) { if (WebUtils.isHttp(subjectContext)) { HttpServletRequest request = WebUtils.getHttpRequest(subjectContext); HttpServletResponse response = WebUtils.getHttpResponse(subjectContext); this.forgetIdentity(request, response); } } private void forgetIdentity(HttpServletRequest request, HttpServletResponse response) { //设置删除标示 response.setHeader(DEFAULT_REMEMBER_ME_HEADER_NAME, "deleteMe"); } }
二. 配置
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"/> <property name="hashIterations" value="1"/> <property name="storedCredentialsHexEncoded" value="true"/> </bean> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"/> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"/> <bean id="rememberMeManager" class="web.shiro.headtoken.HeaderRememberMeManager"> <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/> <!-- <property name="cookie" ref="rememberMeCookie" />--> </bean> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="2592000"/><!-- 30天 --> </bean> <bean id="sessionManager" class="web.shiro.headtoken.DefaultHeaderSessionManager"> <property name="sessionDAO" ref="sessionDAO"/> <!-- <property name="sessionIdCookie" ref="sessionIdCookie"/>--> <property name="globalSessionTimeout" value="3600000"/> <property name="sessionValidationInterval" value="3600000"/> </bean> <bean id="shiroRealm" class="web.shiro.ShiroRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"/> <property name="authenticationCachingEnabled" value="true"/> <property name="authenticationCacheName" value="authenticationCache"/> <property name="authorizationCachingEnabled" value="true"/> <property name="authorizationCacheName" value="authorizationCache"/> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="sessionManager" ref="sessionManager"/> <property name="realm" ref="shiroRealm"/> <property name="cacheManager" ref="cacheManager"/> <!-- 定义RememberMe的管理器 --> <property name="rememberMeManager" ref="rememberMeManager"/> </bean> <bean name="loginFilter" class="web.shiro.filter.JsonAuthLoginFilter" /> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="${shiro.loginUrl}"/> <property name="filters"> <map> <entry key="login" value-ref="loginFilter" /> </map> </property> <property name="filterChainDefinitions"> <value> /logout=logout /user/retrievePwd = anon /user/signOut = anon /user/loginGet = anon /swagger-ui.html = anon /swagger-resources = anon /swagger-resources/** = anon /v2/api-docs = anon /webjars/** = anon /webjars/springfox-swagger-ui/** = anon /user/login = anon /user/register = anon /** = login </value> </property> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
三. 测试方法
1. 通过浏览器登录, 获取到rememberMe
2. 通过postman, 访问接口
这里其实有个问题, 以前这里用cookie的时候, 还能有个cookie的过期时间影响, 比如cookie只保持30天, 那么过了30天之后, 这个cookie不能再使用了, rememberMe就不能使用了.
但是在这里, 却没有这个功能了, 如果想把这个功能加上去, 就必须重写更多的方法来实现这个功能.
比如, 我们可以将过期时间, 加入到 rememberMe中去, 当然, 必须要经过编码才行, 不能太明显. 这样, 来保证rememberMe的过期后, 需要再去登录.