Spring MVC中如何让特定请求不延续用户会话(含AJAX场景)
在Spring MVC中阻止特定请求延续会话的方案
针对你的需求——发送AJAX请求检查会话是否过期,但不让该请求延续会话(即不更新会话的最后访问时间),这里有几种可行的实现方式:
方法一:自定义过滤器还原会话最后访问时间
这是最直接的方案,核心思路是:让容器正常处理请求,然后在请求结束后把会话的最后访问时间还原成请求前的状态,相当于"抵消"了会话延续的操作。
步骤:
- 编写自定义过滤器:
import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.lang.reflect.Field; public class NoSessionExtendFilter implements Filter { // 替换成你的AJAX检查请求的URL private static final String CHECK_SESSION_URL = "/api/check-session"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) request; if (CHECK_SESSION_URL.equals(httpReq.getRequestURI())) { HttpSession session = httpReq.getSession(false); Long originalLastAccessedTime = null; // 记录请求前的会话最后访问时间 if (session != null) { originalLastAccessedTime = session.getLastAccessedTime(); } // 继续执行后续请求处理 chain.doFilter(request, response); // 还原会话最后访问时间(通过反射,因为该方法是protected的) if (session != null && originalLastAccessedTime != null) { try { Field lastAccessedField = session.getClass().getDeclaredField("lastAccessedTime"); lastAccessedField.setAccessible(true); lastAccessedField.set(session, originalLastAccessedTime); } catch (Exception e) { // 处理反射异常,比如打日志 e.printStackTrace(); } } } else { // 其他请求正常处理 chain.doFilter(request, response); } } }
- 在Spring中注册该过滤器:
如果是Spring Boot项目,可以通过@Configuration类注册:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<NoSessionExtendFilter> noSessionExtendFilter() { FilterRegistrationBean<NoSessionExtendFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new NoSessionExtendFilter()); // 设置过滤器优先级,确保在Spring Security等过滤器之前执行 registrationBean.setOrder(1); return registrationBean; } }
方法二:利用容器特性(以Tomcat为例)
Tomcat提供了一个内部属性,可以阻止特定请求更新会话的最后访问时间。你可以在自定义过滤器中设置该属性:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) request; if ("/api/check-session".equals(httpReq.getRequestURI())) { // 设置Tomcat的内部属性,阻止更新会话最后访问时间 httpReq.setAttribute("org.apache.catalina.session.LAST_ACCESS_UPDATED", Boolean.TRUE); } chain.doFilter(request, response); }
注意:这个属性是Tomcat特有的,如果你切换到其他容器(如Jetty),需要查找对应的等效配置。
方法三:Spring Security专属配置(如果使用Spring Security)
如果你的项目使用了Spring Security,可以通过配置会话管理策略,让特定请求不参与会话更新:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 允许检查会话的请求匿名访问 .requestMatchers("/api/check-session").permitAll() .anyRequest().authenticated() ) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 针对特定URL设置不更新会话 .sessionFixation().none() .maximumSessions(1) ); return http.build(); } }
补充:这种方式更适合不需要获取会话状态,只是不想创建新会话的场景。如果需要检查会话是否过期,还是需要结合方法一或方法二,确保既能获取会话,又不更新其过期时间。
关键注意事项
- 如果你只是想检查会话是否存在(而非是否即将过期),可以直接调用
request.getSession(false),但默认情况下,只要请求关联了会话,容器就会更新最后访问时间,所以必须用上述方法阻止这个操作。 - 反射操作需要注意容器兼容性,不同Servlet容器的会话实现类可能不同,方法一的反射代码可能需要根据实际容器调整。
内容的提问来源于stack exchange,提问作者mikach
相关产品推荐
相关产品推荐

