我能否在web.xml中关闭HttpSession?

65

我想完全消除HttpSession - 我可以在web.xml中实现吗?我相信有容器特定的方法可以实现它(这就是当我进行谷歌搜索时拥挤的搜索结果)。

P.S. 这是个坏主意吗?我倾向于在确实需要它们之前完全禁用它们。


4
非常有趣的问题。我遇到一个情况,在使用Tomcat时,我的会话未能被创建,因此我正在寻找是否有一种方法在web.xml中开启会话功能。 :D - Hakanai
9个回答

81

我想要完全消除 HttpSession

你不能完全禁用它。你只需要在你的web应用程序代码中不使用request.getSession()request.getSession(true)获取它的句柄,并确保你的JSPs不会通过设置<%@page session="false"%>来隐式地获取它。

如果你真正关心的是禁用HttpSession背后使用的cookie,那么在Java EE 5 / Servlet 2.5中,你只能在服务器特定的webapp配置中这样做。例如,在Tomcat中,你可以在<Context>元素中将cookies属性设置为false

<Context cookies="false">

还可以查看这个Tomcat特定文档。通过这种方式,如果没有对URL进行重写,会话将不会在随后的请求中保留 - 只有在某些情况下从请求中获取它时才会保留。毕竟,如果您不需要它,那就不要获取它,那么它就根本不会被创建/保留。

或者,如果您已经使用Java EE 6 / Servlet 3.0或更新版本,并且确实想通过web.xml来完成,那么您可以使用以下方法在web.xml中使用新的<cookie-config>元素将最大年龄设置为零:

<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <max-age>0</max-age>
    </cookie-config>
</session-config>
如果您想在Web应用程序中硬编码以使 getSession() 永远不会返回 HttpSession(或者是“空” HttpSession),那么您需要创建一个侦听 url-pattern/* 的过滤器,该过滤器使用 HttpServletRequestWrapper 实现替换 HttpServletRequest,该实现在所有 getSession() 方法上返回null,或者一个什么都不做的虚拟自定义 HttpSession 实现,甚至抛出 UnsupportedOperationException
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
        @Override
        public HttpSession getSession() {
            return null;
        }
        @Override
        public HttpSession getSession(boolean create) {
            return null;
        }
    }, response);
}

附言:这是一个坏主意吗?我更喜欢完全禁用某些功能,直到我真正需要它们。

如果你不需要它们,就不要使用它们。就这样。真的:)


6
HttpServletRequestWrapper听起来不错,我会选择它。在一个有许多开发者的大型项目中,你需要对架构决策(如无状态)进行一些执行。你不能指望人们"仅仅不使用它"。 - barfuin
4
如果你不需要它们,就不要使用它们。并不是那么简单。你的项目使用的几百个库中,有一些可能会在某个地方创建一个会话。 - David Balažic
1
@David:那就直接询问相关的库呗? - BalusC
@Balus,所以没有应用服务器可以处理请求而不创建HttpSession并将数据存储至少一分钟吗?对吗? - Davide Lorenzo MARINO
1
@FranzDeschler:在会话创建中设置一个断点,检查调用堆栈以确定是谁在做这件事以及为什么。 - BalusC
显示剩余7条评论

10
如果您正在构建一个无状态高负载应用程序,您可以像这样禁用使用cookie进行会话跟踪(非侵入式,可能与容器无关):
<session-config>
    <tracking-mode>URL</tracking-mode>
</session-config>

为了执行这个架构决定,请编写类似于下面的内容:

public class PreventSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
    throw new IllegalStateException("Session use is forbidden");
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
    throw new IllegalStateException("Session use is forbidden");
}
}

将其添加到web.xml中,并修复出现该异常的地方:

<listener>
    <listener-class>com.ideas.bucketlist.web.PreventSessionListener</listener-class>
</listener>

7
在使用Java Config的Spring Security 3中,您可以使用HttpSecurity.sessionManagement():
@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

XML的格式如下:

<http create-session="stateless">
  <!-- config -->
</http>

顺便提一下,NEVER和STATELESS之间的区别

NEVER:Spring Security将不会创建HttpSession,但如果HttpSession已经存在,则会使用它。

STATELESS:Spring Security将不会创建HttpSession,并且永远不会使用它来获取SecurityContext。


@ArtemNovikov,你的Spring Security版本是多少?这个功能从3.1版本开始支持。 - Nicholas Lu
这意味着Spring Security永远不会创建HttpSession,但为其他部分的代码留下了开启会话创建的可能性,可以通过使用getSession()来请求会话创建,如BalusC答案所述。 - frankieta

4
我使用以下方法来处理我的RESTful应用程序,以防止创建和使用任何意外的会话cookie。
<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <max-age>0</max-age>
    </cookie-config>
</session-config>

然而,这并不完全关闭HttpSessions。即使会话在一分钟内消失,应用程序仍可能无意中创建会话,而流氓客户端也可能忽略cookie的max-age请求。
这种方法的优点是您无需更改应用程序,只需更改web.xml文件。我建议您创建一个HttpSessionListener,以记录会话何时创建或销毁,以便您可以跟踪其发生时间。

2
我想完全消除HttpSession - 我可以在web.xml中做到这一点吗?我相信有针对容器的特定方法可以实现。
我认为不行。禁用HttpSession将违反Servlet规范,该规范规定HttpServletRequest#getSession应返回会话或创建一个会话。因此,我不希望Java EE容器提供这样的配置选项(这将使其不符合规范)。
这是一个坏主意吗?我喜欢彻底禁用某些东西,直到我真正需要它们。
嗯,我真的不明白重点是什么,如果您不想使用它,请不要将任何内容放入会话中。现在,如果您真的想防止使用会话,则可以使用Filter将请求替换为覆盖getSession()的HttpServletRequestWrapper实现。但我不会浪费时间实现这个 :)。
更新:我的初步建议不够优化,“正确”的(咳咳)方法是替换请求。

2
与其禁用,不如使用URL重写过滤器(例如Tuckey重写过滤器)来重写URL。这样可以使结果对Google友好,同时仍允许基于Cookie的会话处理。
然而,你应该为所有响应禁用它,因为它比仅仅不受搜索引擎欢迎更糟糕。它会暴露会话ID,可用于某些安全漏洞Tuckey过滤器的示例配置
<outbound-rule encodefirst="true">
  <name>Strip URL Session ID's</name>
  <from>^(.*?)(?:\;jsessionid=[^\?#]*)?(\?[^#]*)?(#.*)?$</from>
  <to>$1$2$3</to>
</outbound-rule>

2

从Servlet 3.0开始,您可以通过在ServletContextListenercontextInitialized方法中添加以下代码来使会话不被Servlet容器以任何方式跟踪:

servletContext.setSessionTrackingModes(Collections.emptySet());

Javadoc.


谢谢。使用Jetty工作得很好。我需要一种避免创建会话的方法,而不必确保每个.jsp都配置了<%@ page session="false" %>,到目前为止,这是满足我的需求的最佳解决方案。 - Alexandre Jacob

1

对于RESTful应用程序,我会在每次请求的生命周期结束时简单地使其无效。可能有一些Web服务器,在新客户端访问时始终创建新会话,无论您是否调用request.getSession()


1

无法避免会话创建。但您可以在请求周期结束时检查是否违反了自己的要求。因此,创建一个简单的servlet过滤器,将其放置在第一位,并在chain.doFilter之后抛出异常,如果已创建会话:

chain.doFilter(request, response);
if(request.getSession(false) != null)
    throw new RuntimeException("Somewhere request.getSession() was called");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接