Apache Shiro去掉URL中的JSESSIONID

最近集成框架用到shiro碰到url有时候会带上jsessionid有时候又没有。以前也碰到但是没有深入研究。

网上查了半天各种方法用了都没用。比如web.xml里面加session-config,添加DisableUrlSessionFilter 等等神马都没用。于是自己研究源码。说了半天废话终于进入正题。先申明下我是菜鸟。说的不对的错的请无视。有些是网上复制的。

先说下为什么网上那些玩意没用。

我们一般用的是DefaultWebSessionManager。shiro默认的是ServletContainerSessionManager这个非常简单的实现代表所有会话管理职责(包括会话集群如果 servlet 容器支持)运行 servlet 容器。 它本质上是一个桥 Shiro 会话 API 的 servlet 容器,没有别的。使用这个默认的一个好处是,使用现有的 servlet 容器的应用程序会话配置(超时,任何特定容器集群机制等)将正常工作。这个默认的缺点是,你与 servlet 容器的特定会话行为。 举个例子,如果你想集群会话,但你使用 Jetty 在生产、测试和 Tomcat 容器配置(或代码)将不具有可移植性。所以我们用DefaultWebSessionManager Shiro 的原生会话管理。所以网上的那些基于servlet 的不起作用。如果你用ServletContainerSessionManager也不存在这个问题。(后续有时间我会说说shiro和redis的集成。用redis管理session和cache。我是看了spring session、spring data redis的源码改造的所以跟spring能很好的集成具体后面再说。)

下面分析源码。先说下shiro的每次访问都会构建subject。

假设我们首次访问系统的登陆界面。然后我们登陆。在我们点击登陆之后会访问DefaultSecurityManager构建Subject。创建Subject之前会做很多事(创建cookie和session等等)其中会访问一下DefaultWebSessionManager的getReferencedSessionId方法。方法里面判断cookie里面是否有sessionid。肯定是没有的啦所以不会向request里面存放ShiroHttpServletRequest的3个静态属性。然后在org.apache.shiro.web.filter.authc.FormAuthenticationFilter登陆成功跳转页面的时候。issueSuccessRedirect方法里面会判断request是否有ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE如果有直接跳转到成功页面。具体判断的代码在ShiroHttpServletRequest里面的isRequestedSessionIdFromURL方法上。如果没有会在跳转成功页面url后面加上JSESSIONID。所以url上就多出了JSESSIONID.然后跳转首页时又会访问DefaultSecurityManager构建Subject。同样会进DefaultWebSessionManager的getReferencedSessionId方法这个时候sessionid有值所以request里面存放了

ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE。这就是为什么我们删掉JSESSIONID他又不加上的原因。因为你后面每次访问。构建subject的时候sessionid有值request的里面也有值他就不会在url上加JSESSIONID了啊。假设我们删除浏览器cookie这个时候没有了sessionid。我们访问当前地址。同样会创建Subject。由于sessionid没有了所以request里面就不会存值了。然后会进入org.apache.shiro.web.filter.authc.UserFilter判断subject是否有认证信息。没有会通过saveRequestAndRedirectToLogin跳转到登陆页面。saveRequestAndRedirectToLogin里面判断request是否有值没值又会在跳转页面的url上加上JSESSIONID。然后进入登陆界面的时候构建subject 创建session request里面存值。好了说完了。看不懂得自己脑补把。

private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
String id = getSessionIdCookieValue(request, response);
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
} else {
//not in a cookie, or cookie is disabled - try the request URI as a fallback (i.e. due to URL rewriting):
//try the URI path segment parameters first:
id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
if (id == null) {
//not a URI path segment parameter, try the query parameters:
String name = getSessionIdName();
id = request.getParameter(name);
if (id == null) {
//try lowercase:
id = request.getParameter(name.toLowerCase());
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
}
}
public boolean isRequestedSessionIdFromURL() {
        if (isHttpSessions()) {
            return super.isRequestedSessionIdFromURL();
        } else {
            String value = (String) getAttribute(REFERENCED_SESSION_ID_SOURCE);
            return value != null && value.equals(URL_SESSION_ID_SOURCE);
        }
    }

我的办法在每次跳转之前就判断sessionid是否有值。因为每次跳转之前subject就已经创建了session。至于如果禁用cookies。同样还是会在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;
    }
    @Override
    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;
        }
    }
    @Override
    protected 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;
    }
    @Override
    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
        request.setAttribute(getFailureKeyAttribute(), ae);
    }
    @Override
    protected 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}.
     */
    @Override
    protected 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版本以上这个BUG已经解决只需要在配置文件如下配置中添加红色部分


<!-- 会话管理器 -->

<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">

<property name="sessionIdUrlRewritingEnabled" value="false" />

<!-- 验证会话时会话的过期时间(毫秒) -->

<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-361

request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值