7

在我的 spring 应用程序中,我希望 aSecurityContext始终持有一个Authentication. 如果它不是一个常规的UsernamePasswordAuthenticationToken,它将是一个PreAuthenticatedAuthenticationToken描述“系统用户”。这在需要用户的不同系统功能中是有原因的。如果没有用户上下文,为了避免特殊处理,我只想添加系统上下文。恕我直言,这也与单一责任原则有关。

为此,我可以简单地实现我自己的SecurityContextHolderStrategy并将其设置为SecurityContextHolderwithSecurityContextHolder.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 过滤器链中进行特殊处理。但它应该让我了解我的目标。我正在寻找一种解决方案,该解决方案尽可能无缝地与本机 Spring 安全实现相匹配。我的问题是我对侵入性方法非常固定。这怎么能很好地解决?我无法想象我是第一个有这个要求的人。还是整个概念完全错误?

4

2 回答 2

5

如果得到以下解决方案,它非常光滑并且不会碰撞或干扰任何东西。一般来说,我有两种情况可以进行null身份​​验证:

  1. 主系统线程。
  2. 执行计划任务。(可以MODE_INHERITABLETHREADLOCAL根据用例通过配置解决,更多详细信息见下文。)

1的解决方法。

这仍然留下了主系统线程的问题。这很容易通过在系统启动时设置上下文来处理。此外,我将 配置SecurityContextHolder为使用 a InheritableThreadLocalSecurityContextHolderStrategy,因此所有子线程都将继承SecurityContext. 每次应用程序上下文刷新时,我们都会进行此设置。这允许@DirtiesContext在运行安全上下文相关测试时使用..

@Component
public class SecurityContextConfiguration {

    @EventListener
    public void setupSecurityContext(ContextRefreshedEvent event) {
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    SecurityContextHolder.getContext().setAuthentication(new SystemAuthentication());
    }
}

2的解决方法。

因为我已经配置了SecurityContextHolderMODE_INHERITABLETHREADLOCAL。一个预定的线程将继承他的 parent Securitycontext。在我的用例中,这是不需要的,因为这意味着以下内容:如果计划任务因用户操作而被初始化,它将在 users 下运行SecurityContext。由于我不想在系统重新启动时丢失计划任务,因此我将保留它们。这将导致与用户初始化之前的相同任务SecurityContext将在重新启动时与系统一起初始化SecurityContext。这会产生不一致。因此我也配置了我的调度程序。

我只需将@Scheduled注释配置为通过DelegatingSecurityContextScheduledExecutorService允许我设置 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 上下文。

于 2017-11-10T08:29:50.167 回答
1

createEmptyContext()在:o中创建填充上下文听起来不正确

如此处所述 “一旦请求通过身份验证,身份验证通常将存储在由正在使用的身份验证机制的 SecurityContextHolder 管理的线程本地 SecurityContext中。”,我宁愿扩展UsernamePasswordAuthenticationFilter和覆盖attemptAuthentication设置如果PreAuthenticatedAuthenticationToken用户名密码验证失败。

编辑

我认为对于系统内部任务,它取决于它们的执行方式/执行方式。对于Executor,有一个示例在运行这些执行的线程中如上所述设置上下文:

@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;
}

创建这个@Configurationbean 实现SchedulingConfigurer

于 2017-11-03T06:44:01.313 回答