当前位置: 首页 > 知识库问答 >
问题:

Spring在线程或不同请求方面的SecurityContext行为是什么?

吕嘉荣
2023-03-14

我将介绍Spring Security的不同类实现。我知道我们将身份验证对象设置为SecurityContext ThreadLocal对象,如下所示:

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

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

因此,基本上每个线程都有一个单独的SecurityContext ThreadLocal对象副本,它保存该线程的身份验证对象。在这之前都很好。我在安全配置中将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身份验证对象
  2. 或者,它是特定于HTTP会话的吗?i、 e.对于用户的会话,只有一个线程,因此只有一个安全上下文

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

  1. 对于上面的1,如果它随每个请求而改变,那么为什么我们需要在每个请求的线程中检查身份验证对象,如下所示。我的意思是,如果它是一个不同的线程,没有这个必要。它肯定是空的。(如果我所指的应用程序中存在以下条件)
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);
        }
    }
}

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

共有2个答案

梁宪
2023-03-14

>

  • 如果不知道这个if语句发生在哪里,很难评论它是否不必要。如果请求不需要身份验证,身份验证可能为空,但可能还有其他情况。

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

    线程不绑定到给定的用户会话。使用servlet,线程池中的一个线程被分配给每个HTTP请求。

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

  • 利永年
    2023-03-14

    SecurityContextHolder、SecurityContext和身份验证对象

    默认情况下,SecurityContextHolder使用ThreadLocal来存储这些详细信息,这意味着安全上下文始终可用于同一执行线程中的方法。如果在处理当前主体的请求后注意清除线程,以这种方式使用ThreadLocal是非常安全的。当然,Spring Security会自动为您处理这一点,因此无需担心。

    一些应用程序并不完全适合使用ThreadLocal,因为它们处理线程的特定方式。例如,Swing客户端可能希望Java虚拟机中的所有线程都使用相同的安全上下文。SecurityContextHolder可以在启动时配置策略,以指定您希望如何存储上下文。对于独立应用程序,您将使用SecurityContextHolder.MODE_GLOBAL策略。其他应用程序可能希望由安全线程生成的线程也假设相同的安全标识。这是通过使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现的。您可以通过两种方式从默认的SecurityContextHolder.MODE_THREADLOCAL更改模式。

    第一个是设置系统属性,第二个是调用SecurityContextHolder上的静态方法。大多数应用程序不需要更改默认设置,但如果需要,请查看JavaDoc for SecurityContextHolder以了解更多信息。

    在请求之间存储SecurityContext

    在Spring Security中,在请求之间存储SecurityContext的责任属于SecurityContextPersistenceFilter,默认情况下,它将上下文存储为HTTP请求之间的HttpSession属性。对于每个请求,它将上下文恢复到SecurityContextHolder,最重要的是,在请求完成时清除SecurityContextHolder

    许多其他类型的应用程序(例如,无状态RESTful web服务)不使用HTTP会话,并且会在每次请求时重新进行身份验证。但是,链中应包含SecurityContextPersistenceFilter,以确保每次请求后清除SecurityContextHolder

    会话管理

    .sessionManagement()
         .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    

    将使用NullSecurityContextRepository而不是默认的HttpSessionSecurityContextRepository实现Spring Security性。

    这是一个简单的实现,因为它不会将任何内容保存到HTTP会话,并且对于每个请求,都会创建一个全新的空SecurityContext,因此没有存储的身份验证等。

    更新

    这意味着,如果会话策略是无状态的,则以下条件始终为真。if(SecurityContextHolder.getContext(). getAuthentication()==null)

    是的,除非在调用条件之前设置了身份验证,否则您将获得空身份验证。如果您使用的是JWT令牌,您可以进行如下验证,并可以设置安全上下文。

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String jwt = resolveToken(httpServletRequest);
    
        if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
            Authentication authentication = this.tokenProvider.getAuthentication(jwt);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            ...
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
    
    private String resolveToken(HttpServletRequest request){
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
    
     类似资料:
    • 问题内容: 我想在其他线程中使用Android Volley库发出请求。 我的意思是,线程中存在连接,UI线程中处理了数据。 我要这样做是因为我有很多连接,要处理的数据很多,现在用户界面被阻止了。 那么,如何在不同的线程中创建和启动连接,然后在UIThread中执行OnResponse()/ OnErrorResponse()? 问题答案: Volley执行的每个网络请求都在后台线程中执行。凌空在

    • 我有一个Java web应用程序,使用SPRING进行REST调用。 我想控制应用程序为请求打开的线程数。 所以我添加了线程配置: 我使用的同步服务不是异步的,我对它进行了测试,它没有限制处理请求的线程,而是同时处理所有请求。 我所期望的是,当我一次发送两个请求时,要么抛出第二个请求,要么等待第一个请求完成。 我根本没有在我的应用程序中实现Thread。这是我的控制器的相关代码: 这是控制台结果:

    • 问题内容: 我有几种用注释的方法。 在应用程序上下文中,我具有以下注释驱动的设置: 问题是,有时某些方法的执行会延迟几秒钟甚至几分钟。 我假设即使某个方法需要一段时间才能完成执行,其他方法仍会执行。所以我不明白延迟。 有没有办法减少甚至消除延迟? 问题答案: 为了完整起见,以下代码显示了使用java config配置调度程序的最简单方法: 当需要更多控制时,可以实现一个类。

    • 本文向大家介绍请为什么说js是单线程,而不是多线程呢?相关面试题,主要包含被问及请为什么说js是单线程,而不是多线程呢?时的应答技巧和注意事项,需要的朋友参考一下 JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。 JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript

    • 我正在运行一个spring boot应用程序,刚刚开始从Spring-Cloud-Netflix集成Hystrix。我正在使用@HystrixCommand包装一个用假客户机进行的服务到服务调用。 我的问题是,spring是否提供了将Spring Security上下文(和应用程序上下文)传递给运行Hystrix命令的Hystrix线程的方法?