自学内容网 自学内容网

有/无状态会话 与 Session 与 Spring Security

一、会话(Session)的基本概念

会话是服务器与客户端之间持续交互的机制。在 Web 应用中,每当用户通过浏览器访问应用时,服务器通常会为其创建一个唯一的会话标识(Session ID),并通过 Cookie(通常是 JSESSIONID)将该标识返回给客户端。客户端在后续的每次请求中都会携带这个 Session ID,从而使服务器能够识别出该请求属于哪个用户的会话。

二、Spring Security 如何使用 Session 维持认证状态(有状态会话)

在默认配置下,Spring Security 利用 HTTP Session 来存储用户的认证信息。这意味着一旦用户通过认证(例如通过表单登录),其认证状态会被保存到 Session 中,从而在后续的请求中无需重新认证。

(1)认证成功后的认证信息存储

当用户成功通过认证(例如通过表单登录或其他认证方式),Spring Security 会执行以下步骤:

  • 创建 Authentication 对象: 认证成功后,Spring Security 会生成一个已认证的 Authentication 对象,包含用户的详细信息和权限。

  • 存储到 SecurityContextHolder

    SecurityContext context = SecurityContextHolder.createEmptyContext();
    context.setAuthentication(authResult); // authResult 是已认证的 Authentication 对象
    SecurityContextHolder.setContext(context);
    
  • 存储到 HTTP Session: Spring Security 的 SecurityContextPersistenceFilter 会将 SecurityContextHolder 中的 SecurityContext 保存到 HTTP Session 中。这样,用户的认证状态在会话期间保持有效。

    // SecurityContextPersistenceFilter 内部逻辑示例
    HttpSession session = request.getSession(false);
    if (session != null) {
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);
    }
    

(2)每次请求时加载认证信息

当用户在同一会话中发起新的请求时,Spring Security 会执行以下步骤:

  • HTTP Session 中加载 SecurityContextSecurityContextPersistenceFilter 会在请求开始时,从 HTTP Session 中提取存储的 SecurityContext 对象,并将其设置到 SecurityContextHolder 中。

    // SecurityContextPersistenceFilter 内部逻辑示例
    HttpSession session = request.getSession(false);
    if (session != null) {
        SecurityContext context = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
        if (context != null) {
            SecurityContextHolder.setContext(context);
        }
    }
    
  • 访问 SecurityContextHolder: 任何在处理请求的代码(如控制器、服务层、授权注解等)都可以通过 SecurityContextHolder.getContext().getAuthentication() 来获取当前用户的认证信息,无需重新认证。

(3)请求结束后的认证信息保存

请求处理完成后,SecurityContextPersistenceFilter 会将当前线程的 SecurityContext 再次保存回 HTTP Session,以确保认证信息在会话中持续有效。

// SecurityContextPersistenceFilter 内部逻辑示例
HttpSession session = request.getSession(false);
if (session != null) {
    session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);
}

三、相关组件详解

(1)SecurityContextHolder

  • 作用:是 Spring Security 用来存储 SecurityContext 的核心类。SecurityContext 中包含了当前用户的 Authentication 对象。

  • 存取方式:通过静态方法 SecurityContextHolder.getContext()SecurityContextHolder.setContext() 来获取和设置 SecurityContext

  • 存储策略:默认使用 ThreadLocal 存储,确保每个线程(即每个请求)都有自己的安全上下文(SecurityContext)

(2)SecurityContextPersistenceFilter

  • 作用:负责在每次请求开始时从 HTTP Session 中加载 SecurityContext,并在请求结束时将 SecurityContext 保存回 Session。

  • 执行顺序:位于过滤器链的较前位置,确保在认证和授权之前加载认证信息。

  • 配置:默认情况下,Spring Security 自动注册此过滤器。通常不需要手动配置,除非有特殊需求。

(3)HttpSessionSecurityContextRepository

  • 作用SecurityContextPersistenceFilter 使用的存储机制,负责将 SecurityContext 存储到 HTTP Session 中。

  • 配置:默认使用 HttpSessionSecurityContextRepository,但也可以自定义实现,比如无状态认证(详见后文)。

四、无状态认证与 Session 管理

尽管默认情况下 Spring Security 使用 Session 来维持认证状态,但在某些场景下(如 RESTful API 或微服务架构),可能更倾向于无状态认证方式(例如基于 JWT)。在这种情况下,可以配置 Spring Security 使其不依赖于 HTTP Session,而是通过每个请求携带的 Token 来进行认证。

配置无状态认证的做法
  • 禁用 Session 管理

    http
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        // 其他配置
    

    SessionCreationPolicy.STATELESS 表示 Spring Security 不会创建或使用 HTTP Session。

  • 移除 SecurityContextPersistenceFilter

    在无状态模式下,SecurityContextPersistenceFilter 不再需要,因为不使用 Session 来存储 SecurityContext。但通常通过设置 SessionCreationPolicy.STATELESS,框架会自动调整过滤器链。

  • 使用 Token 过滤器

    自定义一个过滤器(如 JwtAuthenticationFilter)来从每个请求中提取 Token(如 JWT),验证其有效性,并将认证信息设置到 SecurityContextHolder 中。

    public class JwtAuthenticationFilter extends OncePerRequestFilter {
        private final AuthenticationManager authenticationManager;
        private final JwtService jwtService;
    
        public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtService jwtService) {
            this.authenticationManager = authenticationManager;
            this.jwtService = jwtService;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                // 验证 Token 并构建 Authentication 对象
                Authentication auth = jwtService.parseToken(token);
                if (auth != null) {
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            }
            filterChain.doFilter(request, response);
        }
    }
    

五、会话保持的优缺点

优点

  • 简化认证流程:无需在每次请求中重复发送凭证,服务器通过 Session 维护用户状态。
  • 自动管理:Spring Security 自动处理 Session 中的认证信息加载与存储。

缺点

  • 扩展性限制:在分布式系统中,管理和同步 Session 可能变得复杂,需要额外的配置(如分布式 Session 存储)。
  • 资源消耗:每个活跃用户都占用服务器的 Session 资源,可能影响可扩展性。
  • 不适用于无状态服务:对于需要完全无状态的 RESTful API,更推荐使用基于 Token 的认证方式。

原文地址:https://blog.csdn.net/m0_73837751/article/details/144325964

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!