[译]使用@Async进行Spring Security上下文传播

1. 简介

在本教程中,我们将重点关注使用 @Async 传播 Spring Security 主体 默认情况下,Spring Security 身份验证绑定到 ThreadLocal - 因此,当执行流在带有 @Async 的新线程中运行时,它不会是经过身份验证的上下文。

这并不理想------让我们解决它。

2.Maven 依赖

为了在 Spring Security 中使用异步集成,我们需要在 pom.xml 的依赖项中包含以下部分:

xml 复制代码
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.7.3</version>
</dependency>

可以在此处找到最新版本的 Spring Security 依赖项。

3.使用@Async 进行 Spring Security 传播

我们先写一个简单的例子:

less 复制代码
@RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody
public Object standardProcessing() throws Exception {
    log.info("Outside the @Async logic - before the async call: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());

    asyncService.asyncCall();

    log.info("Inside the @Async logic - after the async call: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());

    return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

我们想要检查 Spring SecurityContext 是否传播到新线程。首先,我们在异步调用之前记录上下文,接下来我们运行异步方法,最后再次记录上下文。 asyncCall() 方法具有以下实现:

less 复制代码
@Async
@Override
public void asyncCall() {
    log.info("Inside the @Async logic: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}

正如我们所看到的,只有一行代码将输出异步方法的新线程内的上下文。

4. 默认配置

默认情况下,@Async 方法内的安全上下文将具有空值。

特别是,如果我们运行异步逻辑,我们将能够在主程序中记录 Authentication 对象,但是当我们将其记录在 @Async 中时,它将为 null。这是日志输出的示例:

sql 复制代码
web - 2016-12-30 22:41:58,916 [http-nio-8081-exec-3] INFO
  o.baeldung.web.service.AsyncService -
  Outside the @Async logic - before the async call:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; ...

web - 2016-12-30 22:41:58,921 [http-nio-8081-exec-3] INFO
  o.baeldung.web.service.AsyncService -
  Inside the @Async logic - after the async call:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; ...

  web - 2016-12-30 22:41:58,926 [SimpleAsyncTaskExecutor-1] ERROR
  o.s.a.i.SimpleAsyncUncaughtExceptionHandler -
  Unexpected error occurred invoking async method
  'public void com.baeldung.web.service.AsyncServiceImpl.asyncCall()'.
  java.lang.NullPointerException: null

因此,正如您所看到的,在执行程序线程内,我们的调用失败并出现 NPE,正如预期的那样------因为主体在那里不可用。

5. 异步安全上下文配置

如果我们想要访问异步线程内部的主体,就像我们可以在外部访问它一样,我们需要创建 DelegatingSecurityContextAsyncTaskExecutor bean:

csharp 复制代码
@Bean
public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(ThreadPoolTaskExecutor delegate) {
    return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
}

通过这样做,Spring 将在每个 @Async 调用中使用当前的 SecurityContext

现在,让我们再次运行该应用程序并查看日志信息以确保情况确实如此:

sql 复制代码
web - 2016-12-30 22:45:18,013 [http-nio-8081-exec-3] INFO
  o.baeldung.web.service.AsyncService -
  Outside the @Async logic - before the async call:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; ...

web - 2016-12-30 22:45:18,018 [http-nio-8081-exec-3] INFO
  o.baeldung.web.service.AsyncService -
  Inside the @Async logic - after the async call:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; ...

web - 2016-12-30 22:45:18,019 [SimpleAsyncTaskExecutor-1] INFO
  o.baeldung.web.service.AsyncService -
  Inside the @Async logic:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; ...

正如我们所期望的,我们在异步执行器线程中看到了相同的原理。

6. 使用案例

有一些有趣的用例,我们可能希望确保 SecurityContext 像这样传播:

  • 我们想要发出多个可以并行运行并且可能需要大量时间来执行的外部请求
  • 我们需要在本地进行一些重要的处理,并且我们的外部请求可以与该处理并行执行
  • 其他代表即发即忘场景,例如发送电子邮件

7.结论

在本快速教程中,我们介绍了 Spring 对使用传播的 SecurityContext 发送异步请求的支持。从编程模型的角度来看,新功能看似简单。

请注意,如果先前以同步方式将多个方法调用链接在一起,则转换为异步方法可能需要同步结果。

示例 也可作为 Github 上的 Maven 项目提供。

原文链接:www.baeldung.com/spring-secu...

相关推荐
澄澈青空~4 分钟前
有一个叫R2C,也有一个叫G2C
java·数据库·人工智能·c#
卜夋29 分钟前
Rust 学习笔记 - Day 6: 引用与借用
后端·rust
SarL EMEN1 小时前
海康威视摄像头ISUP(原EHOME协议) 摄像头实时预览springboot 版本java实现,并可以在浏览器vue前端播放(附带源码)
java·前端·spring boot
考虑考虑1 小时前
图片居中
java·后端·java ee
有梦想的攻城狮1 小时前
java中的EnumSet使用详解
java·set·bitset·enumset
逆境不可逃1 小时前
【后端新手谈13】VO、BO、PO、DO、DTO:Java 分层开发的 5 大核心数据对象
java·开发语言
小镇cxy1 小时前
别再把 Git 代理设成全局了
后端
qq_5470261791 小时前
Java 中的 Caffeine 缓存详解
java·开发语言·缓存