javascript
Apache Shiro去掉URL中的JSESSIONID
最近集成框架用到shiro碰到url有時(shí)候會(huì)帶上jsessionid有時(shí)候又沒有。以前也碰到但是沒有深入研究。
網(wǎng)上查了半天各種方法用了都沒用。比如web.xml里面加session-config,添加DisableUrlSessionFilter 等等神馬都沒用。于是自己研究源碼。說了半天廢話終于進(jìn)入正題。先申明下我是菜鳥。說的不對(duì)的錯(cuò)的請(qǐng)無視。有些是網(wǎng)上復(fù)制的。
先說下為什么網(wǎng)上那些玩意沒用。
我們一般用的是DefaultWebSessionManager。shiro默認(rèn)的是ServletContainerSessionManager這個(gè)非常簡(jiǎn)單的實(shí)現(xiàn)代表所有會(huì)話管理職責(zé)(包括會(huì)話集群如果
servlet 容器支持)運(yùn)行 servlet 容器。 它本質(zhì)上是一個(gè)橋 Shiro 會(huì)話 API 的 servlet
容器,沒有別的。使用這個(gè)默認(rèn)的一個(gè)好處是,使用現(xiàn)有的 servlet
容器的應(yīng)用程序會(huì)話配置(超時(shí),任何特定容器集群機(jī)制等)將正常工作。這個(gè)默認(rèn)的缺點(diǎn)是,你與 servlet 容器的特定會(huì)話行為。
舉個(gè)例子,如果你想集群會(huì)話,但你使用 Jetty 在生產(chǎn)、測(cè)試和 Tomcat
容器配置(或代碼)將不具有可移植性。所以我們用DefaultWebSessionManager Shiro
的原生會(huì)話管理。所以網(wǎng)上的那些基于servlet
的不起作用。如果你用ServletContainerSessionManager也不存在這個(gè)問題。(后續(xù)有時(shí)間我會(huì)說說shiro和redis的集成。用redis管理session和cache。我是看了spring
session、spring data redis的源碼改造的所以跟spring能很好的集成具體后面再說。
下面分析源碼。先說下shiro的每次訪問都會(huì)構(gòu)建subject。
假設(shè)我們首次訪問系統(tǒng)的登陸界面。然后我們登陸。在我們點(diǎn)擊登陸之后會(huì)訪問DefaultSecurityManager構(gòu)建Subject。創(chuàng)建Subject之前會(huì)做很多事(創(chuàng)建cookie和session等等)其中會(huì)訪問一下DefaultWebSessionManager的getReferencedSessionId方法。方法里面判斷cookie里面是否有sessionid。肯定是沒有的啦所以不會(huì)向request里面存放ShiroHttpServletRequest的3個(gè)靜態(tài)屬性。然后在org.apache.shiro.web.filter.authc.FormAuthenticationFilter登陸成功跳轉(zhuǎn)頁面的時(shí)候。issueSuccessRedirect方法里面會(huì)判斷request是否有ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE如果有直接跳轉(zhuǎn)到成功頁面。具體判斷的代碼在ShiroHttpServletRequest里面的isRequestedSessionIdFromURL方法上。如果沒有會(huì)在跳轉(zhuǎn)成功頁面url后面加上JSESSIONID。所以u(píng)rl上就多出了JSESSIONID.然后跳轉(zhuǎn)首頁時(shí)又會(huì)訪問DefaultSecurityManager構(gòu)建Subject。同樣會(huì)進(jìn)DefaultWebSessionManager的getReferencedSessionId方法這個(gè)時(shí)候sessionid有值所以request里面存放了
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE。這就是為什么我們刪掉JSESSIONID他又不加上的原因。因?yàn)槟愫竺婷看卧L問。構(gòu)建subject的時(shí)候sessionid有值request的里面也有值他就不會(huì)在url上加JSESSIONID了啊。假設(shè)我們刪除瀏覽器cookie這個(gè)時(shí)候沒有了sessionid。我們?cè)L問當(dāng)前地址。同樣會(huì)創(chuàng)建Subject。由于sessionid沒有了所以request里面就不會(huì)存值了。然后會(huì)進(jìn)入org.apache.shiro.web.filter.authc.UserFilter判斷subject是否有認(rèn)證信息。沒有會(huì)通過saveRequestAndRedirectToLogin跳轉(zhuǎn)到登陸頁面。saveRequestAndRedirectToLogin里面判斷request是否有值沒值又會(huì)在跳轉(zhuǎn)頁面的url上加上JSESSIONID。然后進(jìn)入登陸界面的時(shí)候構(gòu)建subject
創(chuàng)建session request里面存值。好了說完了。看不懂得自己腦補(bǔ)把。
我的辦法在每次跳轉(zhuǎn)之前就判斷sessionid是否有值。因?yàn)槊看翁D(zhuǎn)之前subject就已經(jīng)創(chuàng)建了session。至于如果禁用cookies。同樣還是會(huì)在url上加上jsessionid。我們只是把判斷給至前了。并沒有修改原有邏輯。具體代碼如下
/*** clear JSESSIONID in URL if session id is not null * {@link DefaultWebSessionManager} getReferencedSessionId* {@link ShiroHttpServletRequest} isRequestedSessionIdFromURL* * @author Infinite Justice*/ public class UserFilter extends AccessControlFilter{private Cookie sessionIdCookie;public Cookie getSessionIdCookie() {return sessionIdCookie;}public void setSessionIdCookie(Cookie sessionIdCookie) {this.sessionIdCookie = sessionIdCookie;}@Overrideprotected 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;}}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {saveRequest(request);String sessionid = sessionIdCookie.readValue(WebUtils.toHttp(request), WebUtils.toHttp(response));// clear JSESSIONID in URL if session id is not null if(sessionid != null){request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionid);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);}redirectToLogin(request, response);return false;} } /*** clear JSESSIONID in URL if session id is not null * {@link DefaultWebSessionManager} getReferencedSessionId* {@link ShiroHttpServletRequest} isRequestedSessionIdFromURL* * @author Infinite Justice*/ public class LoginFilter extends FormAuthenticationFilter{private Cookie sessionIdCookie;public Cookie getSessionIdCookie() {return sessionIdCookie;}public void setSessionIdCookie(Cookie sessionIdCookie) {this.sessionIdCookie = sessionIdCookie;}@Overrideprotected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {request.setAttribute(getFailureKeyAttribute(), ae);}@Overrideprotected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {String sessionid = sessionIdCookie.readValue(WebUtils.toHttp(request), WebUtils.toHttp(response));// clear JSESSIONID in URL if session id is not null if(sessionid != null){request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionid);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);}super.issueSuccessRedirect(request, response);} }另一種解決辦法:
public class RedisWebSessionManager extends DefaultWebSessionManager {/*** Stores the Session's ID, usually as a Cookie, to associate with future requests.* @param session the session that was just{@link #createSession created}.*/@Overrideprotected void onStart(Session session, SessionContext context) {super.onStart(session, context);ServletRequest request = WebUtils.getRequest(context);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);}}——————————————————————————————黃金分割線————————————————————————————————————————
如果你的shiro版本在1.3.2版本以上這個(gè)BUG已經(jīng)解決只需要在配置文件如下配置中添加紅色部分
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"><!-- 設(shè)置這個(gè)屬性就可以解決問題--> <property name="sessionIdUrlRewritingEnabled" value="false" /><!-- 驗(yàn)證會(huì)話時(shí)會(huì)話的過期時(shí)間(毫秒) --><property name="globalSessionTimeout" value="3600000" /><property name="sessionFactory" ref="sessionFactory" /><property name="sessionValidationScheduler" ref="redisValidationScheduler" /><property name="sessionDAO" ref="sessionDAO" /><property name="sessionIdCookie" ref="sessionIdCookie" /><property name="sessionListeners"><list><ref bean="redisSessionListener" /></list></property></bean>源代碼中的解釋如下
// always set rewrite flag - SHIRO-361request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());文章轉(zhuǎn)自
總結(jié)
以上是生活随笔為你收集整理的Apache Shiro去掉URL中的JSESSIONID的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多样人群,多面生活——观星盘八大策略人群
- 下一篇: PC版微信,也终于上线了这个超赞的功能