java - 就线程或不同请求而言,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);
}
但是,我对“线程”在这里的含义感到困惑?
- 他们的意思是,与会话没有任何关系的单个 HTTP 请求,即对于每个 HTTP 请求,都会有一个新的 ThreadLocal Authentication 对象?
- 或者,它是否特定于 HTTP 会话?即对于用户的会话,将只有一个线程,因此只有一个安全上下文?
对于上述两点,我也有这两个疑问。
- 对于上面的 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);
}
}
}
- 对于上面的 2,如果我在我的安全配置类中将 SessionCreationPolicy 设置为无状态,那么再次没有会话,但不同线程上的请求不同。
我在这里对线程(ThreadLocal SecurityContext)的解释可能是错误的。需要帮忙。
解决方案
在不知道这个 if 语句发生在哪里的情况下,很难评论它是否没有必要。如果请求不需要认证,认证可能为空,但也可能存在其他情况。
如果请求确实需要身份验证,那么一旦您的 servlet 被调用,身份验证就不应为空。
线程不绑定到给定的用户会话。使用 Servlet,从线程池中为每个 HTTP 请求分配一个线程。
通过
SecurityContextHolder
从会话中提取现有身份验证,或者在您的情况下,从请求数据中提取现有身份验证,为每个请求重新建立。
推荐阅读
- reactjs - 我正在创建一个带有 firebase hosting 的 react Js 应用程序。每次单击按钮时,它都会为 webrtc 创建一个短链接
- mysql - Wordpress 插件在首页触发两次,但在管理员触发一次
- asp.net-core - 如何在 .Net 核心和任何合适的天蓝色资源中处理长时间的处理作业?
- python - 在处理过程中是否可以切换 tkinter 根窗口?
- python - 这个 KeyError 是什么意思?在python中字典的累积键值
- c# - 多名员工 Json 给出反序列化错误
- python - Checkov 错误:AttributeError:类型对象 'Lark' 没有属性 '_load_from_dict
- c - 不包括内核驱动程序
- python - 在 ubuntu 21.04(hirsute) 上需要 python2 libvirt 绑定
- visual-studio-code - 我可以在 webview 中使用 vscode 文件图标吗?