英文:
Using static methods with Spring Security to get current user details
问题
我有一个要求,需要获取已登录的当前用户的详细信息。要获取详细信息,我们可以使用 SecurityContextHolder.getContext()
并提取信息。根据,
SecurityContextHolder、SecurityContext 和 Authentication 对象
默认情况下,SecurityContextHolder
使用 ThreadLocal
来存储这些详细信息,这意味着安全上下文始终对同一执行线程中的方法可用。以这种方式使用 ThreadLocal
是非常安全的,只要在处理当前主体的请求后清除线程即可
。当然,Spring Security会自动为您处理这一点,所以不需要担心。
在Spring Security中,存储 SecurityContext
在请求之间的责任落到了 SecurityContextPersistenceFilter
,默认情况下,它将上下文存储为 HTTP 请求 之间的 HttpSession
属性。它会在每个请求中将上下文恢复到 SecurityContextHolder
,并且在请求完成时清除 SecurityContextHolder
许多其他类型的应用程序(例如,无状态的 RESTful Web 服务)不使用 HTTP 会话,并且会在每次请求时重新认证。然而,重要的是确保在每个请求后清除 SecurityContextHolder
,因此仍然需要在链中包含 SecurityContextPersistenceFilter
。
问题是
我需要在服务层的多个位置获取用户详细信息,以根据用户权限返回信息,因此我们有一个静态方法来返回这些详细信息。该项目包括 REST API 和会话创建策略为 SessionCreationPolicy.STATELESS
,SecurityContextHolderStrategy
为 ThreadLocal
。服务层包括 @Transactional
。
现在考虑对API的多个并发请求,
- Spring Security如何管理 STATELESS 应用程序的
SecurityContextPersistenceFilter
,是否需要手动配置? - 使用
SecurityContextHolderStrategy
作为ThreadLocal
是否可以? - 由于
ThreadLocal
策略和静态方法,是否存在获取错误/无效用户详细信息的机会?
环境:
框架:Spring Boot <br>
ORM:Hibernate <br>
数据库:Postgres <br>
架构:单体应用程序(即将迁移到微服务)<be>
更新:
感谢 @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());
}
sessionManagement
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
注释
SecurityContextHolderStrategy 作为 ThreadLocal
服务层包括 @Transactional
。
英文:
I have a requirement to get the details of the current user who has been loggedIn. To get the details, we can use the SecurityContextHolder.getContext()
and extract the details. According to,
SecurityContextHolder, SecurityContext and Authentication Objects
By default, the SecurityContextHolder
uses a ThreadLocal
to store these details, which means that the security context is always available to methods in the same thread of execution. Using a ThreadLocal
in this way is quite safe if care is taken to clear the thread after the present principal’s request is processed
. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.
Storing the SecurityContext between requests
In Spring Security, the responsibility for storing the SecurityContext
between requests falls to the SecurityContextPersistenceFilter
, which by default stores the context as an HttpSession
attribute between HTTP requests. It restores the context to the SecurityContextHolder
for each request and, crucially, clears the SecurityContextHolder when the request completes
Many other types of applications (for example, a stateless RESTful web service) do not use HTTP sessions and will re-authenticate on every request. However, it is still important that the SecurityContextPersistenceFilter
is included in the chain to make sure that the SecurityContextHolder
is cleared after each request.
Question is
I required the user details at multiple places at the service layer to return the information based on the user authority, So we have one static method which returns these details. The project consists of REST APIs and session creation policy as SessionCreationPolicy.STATELESS
with SecurityContextHolderStrategy
as ThreadLocal
. The service layer consists of @Transactional
.
Now consider the multiple concurrent requests to the APIs,
- How Spring Security will manage
SecurityContextPersistenceFilter
for STATELESS applications, is manual configurations are required? - Is it okay to use
SecurityContextHolderStrategy
asThreadLocal
? - Are there any chances to get wrong/invalid user details due to the
ThreadLocal
strategy and static method?
Environment:
Framework: Spring Boot <br>
ORM: Hibernate <br>
Database: Postgres <br>
Architecture: Monolithic (which is going to migrate to microservices) <be>
I will add more details if required
UPDATE:
Thanks @Macro, as mentioned, for SessionCreationPolicy.STATELESS
,
SessionManagementConfigurer
consist of isStateless()
method which return true for stateless policy. Based on that http set the shared object with NullSecurityContextRepository
and for request cache NullRequestCache
. Hence no value will be available within HttpSessionSecurityContextRepository
. So there might not be issue with invalid/wrong details for user with static method
- Does
SecurityContextHolderStrategy
have any impact on the user details with MODE_INHERITABLETHREADLOCAL, MODE_THREADLOCAL as no value will be set to shared object withHttpSessionSecurityContextRepository
?
Code:
if (stateless) {
http.setSharedObject(SecurityContextRepository.class,
new NullSecurityContextRepository());
}
if (stateless) {
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
Code:
static method to get user details
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());
}
sessionManagement
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Notes
SecurityContextHolderStrategy as ThreadLocal
The service layer consists of @Transactional
.
答案1
得分: 2
设置
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
将会导致Spring Security使用NullSecurityContextRepository
,而不是默认的HttpSessionSecurityContextRepository
。
这是一个简单的实现,它只会在HTTP会话中简单地不保存任何内容,并且对于每个请求,创建一个完全的“新的和空的SecurityContext”,因此没有存储的身份验证等信息。
所以,回答你的第一个问题:
SecurityContextPersistenceFilter
将会像使用HttpSessionRepository
一样工作,没有其他手动配置需要。
回答你的第二个和第三个问题,关于Threadlocals。
是的,这就是使用它们的整个目的,而且这是默认的策略。因此,每个线程都有自己的SecurityContext / Authentication对象。你正在通过静态方法访问ThreadLocal,因此没有机会访问“错误”的用户详细信息。在你的用例中使用“GlocalSecurityContextHolderStrategy”将会是一个问题,但这不是你正在做的事情。
还要记住,所有这些都与@Transactional
无关。
英文:
Setting
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
will lead to Spring Security using a NullSecurityContextRepository
, instead of the default HttpSessionSecurityContextRepository
.
It is a simple implementation, in that it will simply not save anything to the HTTP Session and, for every request, create a completely new and empty SecurityContext
, hence with no stored authentication etc.
So, answering your first question:
> SecurityContextPersistenceFilter will just work as with the
> HttpSessionRepository, there's no other manual configuration required.
To answer your second and third question, regarding 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.
Also, keep in mind that all of this has nothing to do with @Transactionals.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论