java - 具有默认系统身份验证/用户的 SecurityContext

标签 java spring security-context

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

为了实现这一点,我可以简单地实现我自己的 SecurityContextHolderStrategy 并将其设置为 SecurityContextHolderSecurityContextHolder.setStrategyName(MyStrategyClassName);

现在解决问题:

默认的 SecurityContextHolderStrategyThreadLocalSecurityContextHolderStrategy。我对这个策略及其运作方式感到满意。我唯一要更改的是 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 的默认设置?

更新

我上面的方法显然不是解决方案,因为它极具侵入性,会创建冗余代码并且需要在网络过滤器链中进行特殊处理。但它应该让我了解我的目标。 我正在寻找一种解决方案,它尽可能无缝地适合 native spring 安全实现。 我的问题是我非常喜欢侵入性方法。这怎么能很好地解决呢?我无法想象我是第一个有这个要求的人。或者整个概念完全错误?

最佳答案

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

  1. 主系统线程。
  2. 执行计划任务。 (可以根据用例使用 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 上下文。

关于java - 具有默认系统身份验证/用户的 SecurityContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47078381/

相关文章:

vb.net - 在 vb.net 中以不同用户身份运行新进程

java - 在任何数据库上处理大量事务的方法?

java - 线程安全对象数组

java - 如何使自定义异常处理程序正常工作

java - JUnit spring无法使用@Autowired注入(inject)位于其他项目中的相关bean

java - 拆分和解析公式字符串

java - 如何在定制的 AOP 顾问程序中获取 HttpServletRequest?

java - 在 spring java 程序中获取消息 "The type org.aopalliance.aop.Advice cannot be resolved."

security - 在父上下文与子上下文中声明 Spring Bean