我需要获取已登录的当前用户的详细信息。要获取详细信息,我们可以使用 SecurityContextHolder.getContext()
并提取细节。根据,
SecurityContextHolder, SecurityContext and Authentication Objects
默认情况下,SecurityContextHolder
使用 ThreadLocal
存储这些详细信息,这意味着安全上下文始终可用于同一执行线程中的方法。使用 ThreadLocal
这样是quite safe if care is taken to clear the thread after the present principal’s request is processed
.当然,Spring Security 会自动为您处理此问题,因此无需担心。
Storing the SecurityContext between requests
在 Spring Security 中,负责存储 SecurityContext
请求之间属于 SecurityContextPersistenceFilter
,默认情况下将上下文存储为 HttpSession
HTTP 请求之间的属性。它将上下文恢复到 SecurityContextHolder
对于每个请求,最重要的是,在请求完成时清除 SecurityContextHolder
许多其他类型的应用程序(例如, 无状态 RESTful Web 服务)不使用 HTTP session ,并且将对每个请求重新进行身份验证。但是,SecurityContextPersistenceFilter
仍然很重要。包含在链中以确保 SecurityContextHolder
每次请求后清除。
问题是
我要求服务层的多个地方的用户详细信息根据用户权限返回信息,所以我们有一个静态方法来返回这些详细信息。该项目由 REST API 和 session 创建策略组成,如 SessionCreationPolicy.STATELESS
与 SecurityContextHolderStrategy
如ThreadLocal
.服务层由@Transactional
组成.
现在考虑对 API 的多个并发请求,
SecurityContextPersistenceFilter
对于无状态应用,是否需要手动配置? SecurityContextHolderStrategy
吗如ThreadLocal
? ThreadLocal
,是否有可能获取错误/无效的用户详细信息?策略和静态方法? 环境:
框架:Spring Boot
ORM: hibernate
数据库:Postgres
架构:单体(将迁移到微服务)
如果需要,我将添加更多详细信息
更新:
如上所述,感谢@Macro
SessionCreationPolicy.STATELESS
,SessionManagementConfigurer
由 isStateless()
组成对于无状态策略返回 true 的方法。基于该 http 将共享对象设置为 NullSecurityContextRepository
对于请求缓存 NullRequestCache
.因此,HttpSessionSecurityContextRepository
内没有可用的值。 .因此,使用静态方法的用户可能不会出现无效/错误详细信息的问题SecurityContextHolderStrategy
使用 MODE_INHERITABLETHREADLOCAL、MODE_THREADLOCAL 对用户详细信息有任何影响,因为 HttpSessionSecurityContextRepository
不会为共享对象设置任何值? 代码:
if (stateless) {
http.setSharedObject(SecurityContextRepository.class,
new NullSecurityContextRepository());
}
if (stateless) {
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
代码:
获取用户详细信息的静态方法
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
}
private static String extractPrincipal(Authentication authentication) {
if (authentication == null) {
return null;
} else if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
}
public static Optional<Authentication> getAuthenticatedCurrentUser() {
log.debug("Request to get authentication for current user");
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication());
}
session 管理
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
备注
SecurityContextHolderStrategy 为
ThreadLocal
服务层由
@Transactional
组成.
最佳答案
环境
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
将导致 Spring Security 使用
NullSecurityContextRepository
, 而不是默认的 HttpSessionSecurityContextRepository
.这是一个简单的实现,因为它根本不会将任何内容保存到 HTTP session ,并且对于每个请求,创建一个完整的
new and empty SecurityContext
,因此没有存储的身份验证等。所以,回答你的第一个问题:
SecurityContextPersistenceFilter will just work as with the HttpSessionRepository, there's no other manual configuration required.
回答关于 Threadlocals 的第二个和第三个问题。
Yes, that's the whole point of using them and it's the default strategy anyway. So, every thread has its own SecurityContext / Authentication objects. You are accessing the ThreadLocal with your static method, hence there's no chance of accessing "wrong" user details. It would be a problem to use the "GlocalSecurityContextHolderStrategy" in your use-case, but that's not what you are doing, anyway.
另外,请记住,所有这些都与@Transactionals 无关。
关于java - 使用 Spring Security 的静态方法获取当前用户详细信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61555559/