上一篇提到了, 將 sessionId 放到請求頭中去, 那rememberMe是否也能夠放到請求頭中去呢.html
其實無論是sessionId仍是rememberMe, shiro都會默認往cookie裏面放, 那麼rememberMe確定也是能夠放到請求頭中去的.java
有興趣的朋友能夠去看看 org.apache.shiro.web.mgt.CookieRememberMeManager 的實現. web
廢話很少說了, 直接上實現吧.spring
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. 經過瀏覽器登陸, 獲取到rememberMeapache
2. 經過postman, 訪問接口api
這裏其實有個問題, 之前這裏用cookie的時候, 還能有個cookie的過時時間影響, 好比cookie只保持30天, 那麼過了30天以後, 這個cookie不能再使用了, rememberMe就不能使用了. 瀏覽器
可是在這裏, 卻沒有這個功能了, 若是想把這個功能加上去, 就必須重寫更多的方法來實現這個功能. cookie
好比, 咱們能夠將過時時間, 加入到 rememberMe中去, 固然, 必需要通過編碼才行, 不能太明顯. 這樣, 來保證rememberMe的過時後, 須要再去登陸.session