转载

记一次与Shiro有关的错误,404导致session丢失需要重新登录

前段时间上司突然叫我帮忙解决老项目上的一个bug,出现的问题是不同用户账号,进入同一个页面,有个别用户刷新一下当前页面就会重定向到登录页面,需要重新登录。

这是一个几年前的一个项目,使用的是Srping + Spring MVC + Shiro + Jsp的项目,之前没用过Shiro,所以对Shiro了解得不多。

二 问题排查

打开项目源码,先看进那个页面前时都做了什么处理。这是一个作品评审的页面,所以在返回页面前,分别把作品、评审规则、评委等数据put回了页面。

因为每个用户进入这个页面不同的是那个作品List,所以我优先判断是这里出问题,所以先注释掉,不把作品集合put回页面,注释掉后果然不会再出现重新刷新就会重定向到登录页面的问题。

接着我前往Jsp页面查看,发现也只是一个简单的for循环把作品集合输出,没毛病呀。

<c:forEach items="${worksFileVoList}" var="worksFileVo" varStatus="s">
    <li <c:if test="${s.count == 1}">class="on" data-uuid="${worksFileVo.fileId}"</c:if>><a href="javascript:void(0)" onclick="fileAction(this,'${worksFileVo.fileId}')"><img src="${pageContext.request.contextPath}/res/suffix/${worksFileVo.extension}.jpg"></a>
        <p class="word">${worksFileVo.fileName}</p></li>
</c:forEach>
复制代码

最后我想,该不会是这个图片的请求导致的吧,于是我又把img的src去掉,然后运行发现问题也不会出现了。

我抱着好奇的心理,把代码恢复运行,然后打开F12查看Cookie,发现这位有问题的账号每次进入这个页面时,他的JSESSIONID就会被刷新,然后才导致需要重新登录。仔细观察了一下,这个账号,有个别作品的img是请求不到,是404,因为没这个图片,我补回了这张图片,问题就解决了,但这是个治标不治本的办法。

为什么有个404请求,就导致要重新登录了呢,难受呀,百思不得其解。

三 解决问题

问题原因和解决方法在这位老哥的 集成Shiro后当遇到404错误时会丢失session 文章中写得很清楚了,这里我复述一下。

1 首先如果登录成功,Shiro的DefaultWebSessionManager会默认通过如下方式添加JSESSIONID Cookie到响应:

private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
    if (currentId == null) {
        String msg = "sessionId cannot be null when persisting for subsequent requests.";
        throw new IllegalArgumentException(msg);
    } else {
        Cookie template = this.getSessionIdCookie();
        Cookie cookie = new SimpleCookie(template);
        String idString = currentId.toString();
        cookie.setValue(idString);
        cookie.saveTo(request, response);
        log.trace("Set session ID cookie for session with id {}", idString);
    }
}
复制代码

2 如果客户端访问时会带着个Cookie回来;但是注意:容器不认识的(Web容器并没有真正创建HttpSession);Shiro默认情况下会生成自己的一套Session,默认是MemorySessionDAO;即放到内存中的;和Web容器没有任何关系;

3 接着访问一个错误的页面(如jsp);此时到了Shiro过滤器,过滤器通过;然后最后forward到这个错误页面;大家应该知道默认情况下jsp页面是需要session的;所以此时jsp会调用request.getSession(),此时创建了一个Session;这会往Cookie写JSESSIONID的。

解决方法:

1、换一个新的session key,如uid; 推荐这种做法;

2、错误页面 设置<%@ page session="false' %>;

3、给shiro filter配置ERROR,然后在其filterChainDefinitions中添加/WEB-INF/jsp/error/error = anon;

我这里使用第一种方法解决问题

<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    
    <!-- 额外添加多的配置 -->
    <property name="sessionIdCookieEnabled" value="true"/>
    <property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>

<!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
        当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    <constructor-arg name="name" value="new.session.id"/>
</bean>
复制代码

四 总结

本次排错,虽然很快就找到了问题所在,但是最终在思考原因时,因为对Shiro接触得少,并没有往这方面想,就是见识限制了自己的想象,望勤能补拙。

原文  https://juejin.im/post/5be2d982f265da614a39f968
正文到此结束
Loading...