如何在tomcat中禁用JSESSIONID

首先这是一个“伪话题”,原因有下面2点:
1. 现在的应用部署越来越多采用分布式集群部署,从而对于session的使用越来越少。采用session来存储数据的方式逐渐都转移到了缓存中实现。
2. 前后端分离的应用开发模式越来越多,而 jsp 的使用也是越来越少。

但是,我为什么还是要把这个话题拿出来在这里再叙述一遍呢?原因是看到外面大量关于这个话题的文章,基本上都是错的,为了防止读者受到错误影响,我写下了这篇文章,希望对你有所帮助。

JSESSIONID的产生机制

tomcat只有在程序中使用了 xxx.getSession() 的时候才会创建session(JSESSIONID是它的标识),其他任何时候都不会主动去创建。

这个时候有人问了,我的程序里明确没有调用 xxx.getSession(),为什么还会创建session?原因是用到了jsp作为渲染机制,你去看一下在tomcat的工作目录中所对应的jsp编译好的.class或者也有.java文件,能够看到:session = pageContext.getSession();这样一段代码。这个session也就是jsp几个内置对象中含有session对象的来源。所以,只要你用到了 jsp,那么就会默认产生session。

那不用jsp,而用 servlet 的话,会不会主动创建 session 呢?不会的,因为对于Controller或者Action来说,本质上还是servlet。不会的,除非你自己写 xxx.getSession()。

那我也不用jsp,用其他的模板引擎,比如 freemarker 或者 Thymeleaf,会不会主动创建 session 呢?不会的,除非这个模板引擎中也主动调用xxx.getSession,否则不会。

那我既不用jsp,也不用servlet,用的是 spring的Controller或者 struts 的Action,那会不会主动创建 session 呢?不会的,因为对于Controller或者Action来说,本质上还是servlet。

禁用tomcat的session

通过上面的机制分析,很明显禁用tomcat的session的最好途径就是告诉jsp,不要主动创建session,那么方式是在每个jsp页面中加入:
<%@ page session="false" %>
加好以后,创新一下该jsp页面,会发现编译好的.class或者.java中 session = pageContext.getSession(); 这句话已经消失不见了。
加好以后,在浏览器中清除cookie后,再看一下 JSESSIONID 已经不再出现。
那在每个页面中加入 <%@ page session="false" %>,太麻烦了,一般在jsp工程中,大家都会在开始就用 <%@ include file="xxx.jsp"%> 这样来引入一些每个页面中都需要的元素,那么就把 <%@ page session="false" %> 加入到 xxx.jsp 中。

另一种做法:禁用tomcat的session

上一种方法是非常不错的,但是会出现一些纰漏的地方:
1. 会不会有个别jsp页面没有加入 <%@ page session="false" %>
2. 在servlet中,会不会某个程序员随心写了句 session = request.getSession(),但是 session 却没有去用
为了堵住这些纰漏的地方,还有一种采用连根拔起的方式方法。

import java.io.IOException;

import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.session.ManagerBase;

public class SessionManager extends ManagerBase implements Lifecycle {

    private Session globalSession;

    @Override
    protected synchronized void startInternal() throws LifecycleException {
        super.startInternal();
        setState(LifecycleState.STARTING);
    }

    @Override
    protected synchronized void stopInternal() throws LifecycleException {
        setState(LifecycleState.STOPPING);
    }

    @Override
    public void load() throws ClassNotFoundException, IOException {
    }

    @Override
    public void unload() throws IOException {
    }

    @Override
    public Session createSession(String sessionId) {
        if(globalSession == null) {
            globalSession = super.createSession(sessionId);
        } else {
            globalSession.setValid(true);
        }

        return globalSession;
    }

    @Override
    public Session createEmptySession() {
        if(globalSession == null) {
            globalSession = super.createSession(sessionId);
        } else {
            globalSession.setValid(true);
        }

        return globalSession;
    }

}

SessionManager的原理其实是用同一个 globalSession 来代替每次创建新的 session,不创建这个 session 的话,那么会在访问 jsp 页面的时候报错:Page needs a session and none is available。

还需要注意这个globalSession需要进行激活,否则在tomcat的session失效后,会将此session标注为失效,那么尽管globalSession还是存在的,但是是失效的状态,还是会报错说session不存在。所以globalSession.setValid(true);这句很重要。

然后,在tomcat的server.xml中进行设置,大致如下:

<Context docBase="D:\test" path="/test" reloadable="false" sessionCookieName="yoursessionname">
    <Manager className="xxx.xxx.SessionManager" />
</Context>

上面的sessionCookieName默认是JSESSIONID,其实这个是有点点缺陷的,能够让别人知道你的应用是用java开发的,所以,换个其他的名字会更好一些。

springboot中使用上面例子的SessionManager

上面这个方法是用传统方式开发应用,那么在springboot下,因为内嵌了tomcat,使得更加简便

@Configuration
public class TomcatConfig {

    @Bean
    public EmbeddedServletContainerFactory servletContainer(){
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addContextCustomizers(new TomcatContextCustomizer() {
        @Override
        public void customize(Context context) {
            SessionManager sessionManager = new SessionManager();
            context.setManager(sessionManager);
            context.setSessionCookieName("yoursessionname");
        }
    });
        return factory;
    }
}

这种方式,最好做成 Auto-configuration 的jar,在pom.xml中引入,更加方便,具体可以参考一下这篇文章:@SpringBootApplication中几点解惑

综述

  1. 最好在jsp页面中加入 <%@ page session="false" %>
  2. 用上面的SessionManager方式
    最后说一下,现代的java应用开发,几乎不考虑 session 了,所以这篇文章对您可能没有多大用处,对一些传统应用还是有些帮助的。

发表评论

邮箱地址不会被公开。 必填项已用*标注