是否可以控制哪些请求将在 Spring Boot 中重置会话的不活动状态?

问题描述 投票:0回答:1

在部署为启动独立 WAR(不在应用程序服务器中)的应用程序中使用 Spring Boot (3.2),是否可以控制哪些请求重置会话的不活动状态?

场景: 混合了页面 (JSP) 和 API 的应用程序。我们希望在每个page请求时重置会话不活动状态,但对于API请求。换句话说,对于坐在定期发出 API 请求(轮询)的页面上的用户,用户的会话永远不会超时。我想限制会话不活动,以便它不会被这些 API 请求重置(因此坐在该页面上的用户超时)。

这可能吗?如果是这样,怎么办?或者,指向执行不活动重置的 servlet 过滤器的指针也会很有用。

spring-boot spring-security
1个回答
0
投票

正如评论中提到的,Spring 不参与空闲会话跟踪或超时,它只是将配置传递到嵌入式 Web 服务器(默认为 Tomcat)。

为了实现这一点,我构建了以下类。它的工作原理是将请求包装到“正常”页面(“应该”重置会话超时的请求),以便它可以观察对会话的访问并将该时间记录为会话中的单独属性。对于不应该计入会话活动的请求(由本解决方案中的一组 URL 模式指定),它会根据当前时间检查存储的属性,以查看超时是否已过;如果是这样,它会使会话无效并将请求传递到链上进行处理(这允许 Spring 像任何其他过期/无效会话一样处理它)。 import static java.lang.System.currentTimeMillis; import java.io.IOException; import java.time.Duration; import java.util.Arrays; import java.util.List; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; @Slf4j public class IdleSessionTrackingFilter extends OncePerRequestFilter { private static final String LAST_ACCESS_ATTRIBUTE = "lastAccessTime"; private final Duration idleSessionTimeout; private final List<AntPathRequestMatcher> matchers; public IdleSessionTrackingFilter(Duration idleSessionTimeout, String... patterns) { this.idleSessionTimeout = idleSessionTimeout; this.matchers = Arrays.stream(patterns) .map(AntPathRequestMatcher::antMatcher) .toList(); } public FilterRegistrationBean<IdleSessionTrackingFilter> reigstrationBean() { return new FilterRegistrationBean<>(this); } protected boolean shouldNotKeepAlive(HttpServletRequest request) throws ServletException { return matchers.stream().anyMatch(m -> m.matches(request)); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { boolean normalRequest = !shouldNotKeepAlive(request); if (normalRequest) { log.debug("Non-matching request, processing as normal."); // Wrap the request (to observe session access) and process as normal request = new SessionAccessAwareRequest(request); chain.doFilter(request, response); return; } HttpSession session = request.getSession(false); if (session == null) { log.debug("No session in request to {} {}, processing as normal.", request.getMethod(), request.getRequestURI()); } else { if (isExpired(session)) { log.info("Non-renewing request to {} {} and session is old, invalidating it.", request.getMethod(), request.getRequestURI()); session.invalidate(); } } chain.doFilter(request, response); } protected boolean isExpired(HttpSession session) { Long lastAccessTime = (Long) session.getAttribute(LAST_ACCESS_ATTRIBUTE); if (lastAccessTime == null ) { return false; } long idleTime = currentTimeMillis() - lastAccessTime.longValue(); return idleTime > idleSessionTimeout.toMillis(); } /** * Request wrapper that observes session access via getSession() method and * stores that as our internal last-accessed-time. */ private static class SessionAccessAwareRequest extends HttpServletRequestWrapper { public SessionAccessAwareRequest(HttpServletRequest request) { super(request); } @Override public HttpSession getSession(boolean create) { HttpSession session = super.getSession(create); if (session != null) { session.setAttribute(LAST_ACCESS_ATTRIBUTE, currentTimeMillis()); } return session; } } } 使用示例:

    private static final String ALL_APIS_PATTERN = "/**/api/**";

    @Value("${server.servlet.session.timeout}")
    private Duration sessionTimeout;

    // ...

    @Bean
    FilterRegistrationBean<IdleSessionTrackingFilter> apiRequestSessionKeepAliveFilter() {
        return new IdleSessionTrackingFilter(sessionTimeout, ALL_APIS_PATTERN)
                    .reigstrationBean();
    }

这个解决方案大致基于@pavel-horal 的类似问题的

这个答案
    

© www.soinside.com 2019 - 2024. All rights reserved.