在我的春季申请中,我希望SecurityContext
始终拥有Authentication
。如果它不是常规UsernamePasswordAuthenticationToken
,则它将是描述“系统用户”的PreAuthenticatedAuthenticationToken
。这具有需要用户的不同系统功能的原因。如果没有用户上下文,为了避免特殊处理,我只想添加系统上下文。恕我直言,这也与单一责任原则有关。
为实现这一目标,我可以简单地实现自己的SecurityContextHolderStrategy
并将其设置为SecurityContextHolder
SecurityContextHolder.setStrategyName(MyStrategyClassName);
现在问题:
默认SecurityContextHolderStrategy
是ThreadLocalSecurityContextHolderStrategy
。我很满意这个策略及其运作方式。我唯一要改变的是getContext()
方法。
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()
方法。但这给了我一种感觉,因为我可能走错了路。
我如何才能将“系统用户”Authentication
视为新SecurityContext
的默认设置?
更新
我的上述方法显然不是解决方案,因为它极具侵入性,会产生冗余代码,需要在Web过滤器链中进行特殊处理。但它应该让我理解我的目标。 我正在寻找一种解决方案,它尽可能地与原生弹簧安全实现无缝结合。 我的问题是我对入侵方法非常关注。这怎么能很好地解决?我无法想象我是第一个有此要求的人。或者整个概念完全错了?
答案 0 :(得分:3)
如果得到以下解决方案,这是非常光滑的,不会碰撞或干扰任何事情。
一般情况下,我有两种情况,我将进行null
身份验证:
MODE_INHERITABLETHREADLOCAL
配置解决,更多详细信息请参见下文。)解决方案1。
这仍然是主系统线程的问题。只需在系统启动时设置上下文即可轻松处理。另外,我将SecurityContextHolder
配置为使用InheritableThreadLocalSecurityContextHolderStrategy
,以便所有子线程都将继承SecurityContext
。每次应用程序上下文刷新时,我们都会进行此设置。这允许在运行与安全上下文相关的测试时使用@DirtiesContext
。
@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
进行初始化。这会产生不一致。因此我也配置了我的调度程序。
我只是将@Scheduled
注释配置为由DelegatingSecurityContextScheduledExecutorService
执行,允许我设置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上下文。
答案 1 :(得分:0)
在createEmptyContext()
:o)
如陈述here所述,“一旦请求经过身份验证,身份验证通常会存储在由SecurityContextHolder 管理的线程本地SecurityContext中正在使用的机制。“,我宁愿扩展UsernamePasswordAuthenticationFilter
并覆盖attemptAuthentication
,以便在用户名密码验证失败的情况下设置PreAuthenticatedAuthenticationToken
。
修改强>
我认为对于系统内部任务,它取决于它们执行的方式。
对于Executor
,有一个example设置上下文,如上所述在运行这些执行的线程中:
@Bean
public Executor taskExecutor() {
ScheduledExecutorService delegateExecutor = Executors.newSingleThreadScheduledExecutor();
SecurityContext schedulerContext = createSchedulerSecurityContext();
return new DelegatingSecurityContextScheduledExecutorService(delegateExecutor, schedulerContext);
}
private SecurityContext createSchedulerSecurityContext() {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new PreAuthenticatedAuthenticationToken("system", null);
authentication.setAuthenticated(true);
context.setAuthentication(authentication);
return context;
}
创建此bean的@Configuration
实现SchedulingConfigurer
。