具有默认系统身份验证/用户的SecurityContext [英] SecurityContext with default System authentication/user
问题描述
在我的spring应用程序中,我希望SecurityContext
始终包含Authentication
.如果不是常规的UsernamePasswordAuthenticationToken
,它将是描述系统用户"的PreAuthenticatedAuthenticationToken
.这在需要用户的不同系统功能内具有原因.为了避免在没有用户上下文的情况下进行特殊处理,我只想添加系统上下文.恕我直言,这也与单一责任原则有关.
In my spring application, I would like that a SecurityContext
always holds an Authentication
. If it's not a regular UsernamePasswordAuthenticationToken
, it will be a PreAuthenticatedAuthenticationToken
describing the "system user." This has reasons within different system function which requires a user. To avoid a special treatment if there is no user context, I merely want to add the system context. IMHO, this has also to do with the single responsibility principle.
要实现此目的,我可以简单地实现自己的SecurityContextHolderStrategy
,并使用SecurityContextHolder.setStrategyName(MyStrategyClassName);
To achieve this, I can simply implement my own SecurityContextHolderStrategy
and set the it to the SecurityContextHolder
with SecurityContextHolder.setStrategyName(MyStrategyClassName);
出现问题了:
默认SecurityContextHolderStrategy
是ThreadLocalSecurityContextHolderStrategy
.我对这项策略及其运作方式感到满意.我唯一要更改的是getContext()
方法.
The default SecurityContextHolderStrategy
is the ThreadLocalSecurityContextHolderStrategy
. I'm happy with this strategy and how it works. The only thing which I would change is the getContext()
method.
public SecurityContext getContext() {
SecurityContext ctx = CONTEXT_HOLDER.get();
if (ctx == null) {
ctx = createEmptyContext();
CONTEXT_HOLDER.set(ctx);
}
return ctx;
}
到
public SecurityContext getContext() {
SecurityContext ctx = CONTEXT_HOLDER.get();
if (ctx == null) {
ctx = createEmptyContext();
Authentication authentication = new PreAuthenticatedAuthenticationToken("system", null);
authentication.setAuthenticated(true);
ctx.setAuthentication(authentication);
CONTEXT_HOLDER.set(ctx);
}
return ctx;
}
这不可能,因为ThreadLocalSecurityContextHolderStrategy
类不是 public
.当然,我可以简单地将ThreadLocalSecurityContextHolderStrategy
的代码复制粘贴到我自己的SecurityContextHolderStrategy
中,并以自己想要的方式实现getContext()
方法.但这给了我感觉,好像我走错了路.
This is not possible as the ThreadLocalSecurityContextHolderStrategy
class is not public
. Of course I can simply copy paste the code of the ThreadLocalSecurityContextHolderStrategy
into my own SecurityContextHolderStrategy
and implement the getContext()
method the way I want. But this gives me the feeling as I might be on the wrong path.
我如何获得新的SecurityContext
的默认系统用户" Authentication
?
How could I achieve a "system user" Authentication
as default for a new SecurityContext
?
更新
我的上述方法显然不是一种解决方案,因为它极具侵入性,会创建冗余代码,并且需要在Web过滤器链中进行特殊处理.但这应该使我了解自己的目标. 我正在寻找一种解决方案,该解决方案应尽可能与本机spring安全性实现无缝结合. 我的问题是我对侵入性方法非常了解.如何解决好呢?我无法想象我是这个要求的第一人.还是整个概念完全错误?
My approach above is apparently not a solution as it is extremely invasive, creates redundant code and needs special treatment within the web filter chain. But it should give an understanding of my goal. I'm looking for a solution, which fits as seamless as possible to the native spring security implementation. My problem is that I'm quite fixed on the invasive approach. How can this solve nicely? I cannot imagine that I'm the first person with this requirement. Or is the whole concept altogether wrong?
推荐答案
如果获得了以下解决方案,那么该解决方案非常精巧,不会碰撞或干扰任何东西.
通常,在两种情况下,我将进行null
身份验证:
If got the following solution, which is quite slick and doesn't collide or interfere with anything.
In generall I have two situations where I'll have a null
authentication:
- 主系统线程.
- 执行预定任务. (可以根据具体情况使用
MODE_INHERITABLETHREADLOCAL
配置解决,更多详细信息请参见下文.)
- Main system thread.
- Executing scheduled task. (Could be solved with
MODE_INHERITABLETHREADLOCAL
config depending on use case, more details see below.)
解决方案1.
这仍然使主系统线程出现问题.只需在系统启动时设置上下文,即可非常轻松地处理此问题.另外,我将SecurityContextHolder
配置为使用InheritableThreadLocalSecurityContextHolderStrategy
,以便所有子线程都将继承SecurityContext
.每当应用程序上下文刷新时,我们都会进行此设置.允许在运行与安全上下文相关的测试时使用@DirtiesContext
.
This still leaves the problem with the main system thread. This is very easily handled by just setting the context on system start up. Also I configure the SecurityContextHolder
to use a InheritableThreadLocalSecurityContextHolderStrategy
so all child threads will inherit the SecurityContext
. We make this setting everytime the application context refreshes. This allows to use @DirtiesContext
when running security context related tests..
@Component
public class SecurityContextConfiguration {
@EventListener
public void setupSecurityContext(ContextRefreshedEvent event) {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
SecurityContextHolder.getContext().setAuthentication(new SystemAuthentication());
}
}
解决方案2.
因为我已经用MODE_INHERITABLETHREADLOCAL配置了SecurityContextHolder
.预定线程将影响其父Securitycontext
.在我的用例中,这是不需要的,因为这意味着以下内容:
如果计划的任务是在用户操作期间初始化的,则它将在用户SecurityContext
下运行.由于我不想在系统重新启动时放弃计划的任务,因此我将其保留下来.这将导致与用户SecurityContext
初始化之前相同的任务,在重新启动时将与系统SecurityContext
一起初始化.这产生了矛盾.因此,我也配置了调度程序.
As I have configured the SecurityContextHolder
with MODE_INHERITABLETHREADLOCAL. A scheduled thread will inheriet his parent Securitycontext
. In my use case this is not wanted as this would mean the following:
If a scheduled task gets initialized dua a user action, it would run under the users SecurityContext
. As I do not want to loose a scheduled task on system reboot, I'll persist them. Which would lead to that the same task which was before initialized with the users SecurityContext
, will get intitialize with the systems SecurityContext
on reboot. This generates an inconsitence. Therefor I configure my scheduler too.
我只需将@Scheduled
注释配置为由DelegatingSecurityContextScheduledExecutorService
执行,就可以设置SecurityContext
.
I simply configure the @Scheduled
annotation to be executed by a DelegatingSecurityContextScheduledExecutorService
allowing me to set a SecurityContext
.
@EnableScheduling
@Configuration
public class SystemAwareSchedulerConfiguration implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean
public ScheduledExecutorService taskExecutor() {
ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor();
SecurityContext schedulerContext = createSchedulerSecurityContext();
return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext);
}
private SecurityContext createSchedulerSecurityContext() {
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(new SystemAuthentication());
return securityContext;
}
}
使用这两种配置,如果线程不是由Web容器初始化的,我将始终具有SystemUser上下文.
With this two configurations, I'll always will have a SystemUser context if the thread wasn't initialized by the web container.
这篇关于具有默认系统身份验证/用户的SecurityContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!