You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC中如何让特定请求不延续用户会话(含AJAX场景)

在Spring MVC中阻止特定请求延续会话的方案

针对你的需求——发送AJAX请求检查会话是否过期,但不让该请求延续会话(即不更新会话的最后访问时间),这里有几种可行的实现方式:

方法一:自定义过滤器还原会话最后访问时间

这是最直接的方案,核心思路是:让容器正常处理请求,然后在请求结束后把会话的最后访问时间还原成请求前的状态,相当于"抵消"了会话延续的操作。

步骤:

  1. 编写自定义过滤器:
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);
        }
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:54:23