翻译版本【spring-security 6.2.1】SecurityContextHolder
SecurityContextHolder
Spring Security身份验证模型的核心是SecurityContextHolder。它包含SecurityContext。
SecurityContextHolder是Spring Security存储身份验证详细信息的地方。Spring Security并不关心SecurityContextHolder是如何填充的。如果包含值,则使用该值作为当前认证的用户。
表明用户已通过身份验证的最简单方法是直接设置SecurityContextHolde
设置SecurityContextHolder
SecurityContext context = SecurityContextHolder.createEmptyContext();// 1
Authentication authentication =new TestingAuthenticationToken("username", "password", "ROLE_USER");// 2
context.setAuthentication(authentication);SecurityContextHolder.setContext(context);// 3
- 我们首先创建一个空的SecurityContext。您应该创建一个新的SecurityContext实例,而不是使用SecurityContextHolder.getContext().setAuthentication(authentication)来避免多个线程之间的竞争条件。
- 接下来,我们创建一个新的Authentication对象。Spring Security并不关心在SecurityContext上设置了什么类型的身份验证实现。在这里,我们使用TestingAuthenticationToken,因为它非常简单。更常见的生产场景是使用UsernamePasswordAuthenticationToken(userDetails, password, authorities)。
- 最后,我们在SecurityContextHolder上设置SecurityContext。Spring Security使用此信息进行授权。
要获取有关已验证主体(authenticated principal)的信息,请访问SecurityContextHolder。
访问当前经过身份验证的用户
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
默认情况下,SecurityContextHolder使用ThreadLocal来存储这些详细信息,这意味着SecurityContext始终可用于同一线程中的方法,即使SecurityContext没有明确地作为参数传递给这些方法。如果您在处理当前主体的请求后注意清除线程,那么以这种方式使用ThreadLocal是非常安全的。Spring Security的FilterChainProxy确保始终清除SecurityContext。
有些应用程序并不完全适合使用ThreadLocal,因为它们使用线程的特定方式。例如,Swing客户端可能希望Java虚拟机中的所有线程使用相同的安全上下文。您可以在启动时使用策略配置SecurityContextHolder,以指定希望如何存储上下文。对于独立的应用程序,您可以使用SecurityContextHolder.MODE_GLOBAL模式。其他应用程序可能希望由安全线程派生的线程也采用相同的安全特性。你可以通过使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现这一点。MODE_THREADDLOCAL有两种方式。第一种是设置系统属性。第二种方法是调用SecurityContextHolder上的静态方法。大多数应用程序不需要更改默认值。但是,如果需要,请查看SecurityContextHolder的JavaDoc以了解更多信息。