[译]使用@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...

相关推荐
long3167 分钟前
代理设计模式
java·学习·程序人生·设计模式·代理模式
bobz96511 分钟前
ovs 桥接了 bond0.1234, 链路层功能还在,但 IP 层功能无法使用
后端
渣哥18 分钟前
惊呆!Java深拷贝 vs 浅拷贝,区别竟然这么大!
java
用户27079129381819 分钟前
为什么在 Java 中字符串是不可变的?
java
似水流年流不尽思念20 分钟前
Spring Bean有哪些生命周期回调方法?有哪几种实现方式?
后端·spring·面试
Moonbit27 分钟前
提交即有奖!MGPIC 游戏赛道官方推荐框架上线,直播同步解读赛题。 MoonBit MoonBit
后端·微信·程序员
郝同学的测开笔记27 分钟前
打通回家之路:OpenVPN,你的企业网络万能钥匙(一)
运维·后端·测试
whitepure27 分钟前
万字详解Java代码块
java·后端
忘带键盘了40 分钟前
Dish、DishVO 和 DishDTO
java
SimonKing1 小时前
Spring Boot Admin:一站式监控微服务,这个运维神器真香!
java·后端·程序员