首页 > 解决方案 > 就线程或不同请求而言,Spring 的 SecurityContext 行为是什么?

问题描述

我正在经历 Spring Security 的不同类实现。我知道我们将Authentication对象设置为SecurityContext ThreadLocal 对象:

UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(upat);

因此,基本上对于每个线程,都有一个 SecurityContext ThreadLocal 对象的单独副本,该对象保存该线程的 Authentication 对象。到这里为止很好。我在我的 SecurityConfiguration 中也将 SessionCreationPolicy 设置为无状态。下面是安全配置:

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");

        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        final CorsConfigurer<HttpSecurity> cors = http.csrf().disable().cors().configurationSource(source);

        final ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry exp =
                cors.and().authorizeRequests();

        exp.antMatchers("/getJWTToken/**").permitAll()
                .antMatchers("/actuator/**").permitAll()
                .antMatchers("/rest/**").authenticated();

        exp.and().exceptionHandling()
                .authenticationEntryPoint(authEntryPoint())
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        ;

        // Add a filter to validate the tokens with every request
        http.addFilterBefore(authRequestFilter(), UsernamePasswordAuthenticationFilter.class);
    }

但是,我对“线程”在这里的含义感到困惑?

  1. 他们的意思是,与会话没有任何关系的单个 HTTP 请求,即对于每个 HTTP 请求,都会有一个新的 ThreadLocal Authentication 对象?
  2. 或者,它是否特定于 HTTP 会话?即对于用户的会话,将只有一个线程,因此只有一个安全上下文?

对于上述两点,我也有这两个疑问。

  1. 对于上面的 1,如果它随着每个请求而变化,那么为什么我们需要在每个请求的线程中检查 Authentication 对象,如下所示。我的意思是,如果它是一个不同的线程,则不需要这个。它肯定是空的。(以下 if 条件存在于我所指的应用程序中)。
if( SecurityContextHolder.getContext().getAuthentication() == null ) {
    if( jwtTokenUtil.validateToken(jwtToken, userObj) )
    {
        if( userObj == null )
        {
            response.setStatus(401);
            return;
        }
        else
        {
            UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(userObj, null,userObj.getAuthorities());

            upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            // After setting the Authentication in the context, we specify
            // that the current user is authenticated. So it passes the
            // Spring Security Configurations successfully.
            SecurityContextHolder.getContext().setAuthentication(upat);
        }
    }
}
  1. 对于上面的 2,如果我在我的安全配置类中将 SessionCreationPolicy 设置为无状态,那么再次没有会话,但不同线程上的请求不同。

我在这里对线程(ThreadLocal SecurityContext)的解释可能是错误的。需要帮忙。

标签: javaspringspring-bootspring-securityweb-applications

解决方案


  1. 在不知道这个 if 语句发生在哪里的情况下,很难评论它是否没有必要。如果请求不需要认证,认证可能为空,但也可能存在其他情况。

    如果请求确实需要身份验证,那么一旦您的 servlet 被调用,身份验证就不应为空。

  2. 线程不绑定到给定的用户会话。使用 Servlet,从线程池中为每个 HTTP 请求分配一个线程。

    通过SecurityContextHolder从会话中提取现有身份验证,或者在您的情况下,从请求数据中提取现有身份验证,为每个请求重新建立。


推荐阅读