# 并发支持

在大多数环境中，安全性是以 per`Thread`为基础存储的。这意味着，当在新的`Thread`上完成工作时，`SecurityContext`将丢失。 Spring 安全性提供了一些基础设施，以帮助用户更容易地实现这一点。 Spring 安全性为在多线程环境中使用 Spring 安全性提供了低层次的抽象。事实上，这就是 Spring 安全性构建到与[AsyncContext.start（可运行）](../../servlet/integrations/servlet-api.html#servletapi-start-runnable)和[Spring MVC Async Integration](../../servlet/integrations/mvc.html#mvc-async)集成的基础。

## 在可撤销的情况下将证券转让

Spring Security 的并发支持中最基本的构建块之一是`DelegatingSecurityContextRunnable`。它包装了一个委托`Runnable`，以便用指定的`SecurityContext`为委托初始化`SecurityContextHolder`。然后，它调用委托 Runnable 确保在之后清除`SecurityContextHolder`。`DelegatingSecurityContextRunnable`看起来是这样的：

爪哇

```
public void run() {
try {
	SecurityContextHolder.setContext(securityContext);
	delegate.run();
} finally {
	SecurityContextHolder.clearContext();
}
}
```

Kotlin

```
fun run() {
    try {
        SecurityContextHolder.setContext(securityContext)
        delegate.run()
    } finally {
        SecurityContextHolder.clearContext()
    }
}
```

虽然非常简单，但它可以无缝地将 SecurityContext 从一个线程转移到另一个线程。这一点很重要，因为在大多数情况下，SecurityContextholder 是以每个线程为基础的。例如，你可能使用了 Spring Security 的[\<global-method-security\>](../../servlet/appendix/namespace/method-security.html#nsa-global-method-security)支持来保护你的某个服务。现在，你可以轻松地将当前`Thread`的`SecurityContext`传输到调用安全服务的`Thread`。下面是你如何做到这一点的一个示例：

爪哇

```
Runnable originalRunnable = new Runnable() {
public void run() {
	// invoke secured service
}
};

SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
	new DelegatingSecurityContextRunnable(originalRunnable, context);

new Thread(wrappedRunnable).start();
```

Kotlin

```
val originalRunnable = Runnable {
    // invoke secured service
}
val context: SecurityContext = SecurityContextHolder.getContext()
val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable, context)

Thread(wrappedRunnable).start()
```

上面的代码执行以下步骤：

* 创建将调用我们的安全服务的`Runnable`。请注意，它并不了解 Spring 安全性

* 从`SecurityContextHolder`获取我们希望使用的`SecurityContext`，并初始化`DelegatingSecurityContextRunnable`

* 使用`DelegatingSecurityContextRunnable`创建线程

* 启动我们创建的线程

由于从`SecurityContextHolder`中使用`SecurityContext`创建`DelegatingSecurityContextRunnable`是很常见的，因此它有一个快捷构造函数。以下代码与上述代码相同：

爪哇

```
Runnable originalRunnable = new Runnable() {
public void run() {
	// invoke secured service
}
};

DelegatingSecurityContextRunnable wrappedRunnable =
	new DelegatingSecurityContextRunnable(originalRunnable);

new Thread(wrappedRunnable).start();
```

Kotlin

```
val originalRunnable = Runnable {
    // invoke secured service
}

val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable)

Thread(wrappedRunnable).start()
```

我们拥有的代码使用起来很简单，但仍然需要了解我们正在使用 Spring 安全性。在下一节中，我们将研究如何利用`委派安全环境专家`来隐藏我们正在使用 Spring 安全性的事实。

## DelegatingSecurityContextExecutor

在上一节中，我们发现使用`DelegatingSecurityContextRunnable`很容易，但并不理想，因为我们必须意识到 Spring 安全性才能使用它。让我们来看看`DelegatingSecurityContextExecutor`如何保护我们的代码不受我们正在使用 Spring 安全性的任何知识的影响。

`DelegatingSecurityContextExecutor`的设计与`DelegatingSecurityContextRunnable`的设计非常相似，只是它接受一个委托`Executor`而不是一个委托`Runnable`。你可以在下面看到一个如何使用它的示例：

爪哇

```
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
	new UsernamePasswordAuthenticationToken("user","doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"));
context.setAuthentication(authentication);

SimpleAsyncTaskExecutor delegateExecutor =
	new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
	new DelegatingSecurityContextExecutor(delegateExecutor, context);

Runnable originalRunnable = new Runnable() {
public void run() {
	// invoke secured service
}
};

executor.execute(originalRunnable);
```

Kotlin

```
val context: SecurityContext = SecurityContextHolder.createEmptyContext()
val authentication: Authentication =
    UsernamePasswordAuthenticationToken("user", "doesnotmatter", AuthorityUtils.createAuthorityList("ROLE_USER"))
context.authentication = authentication

val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = DelegatingSecurityContextExecutor(delegateExecutor, context)

val originalRunnable = Runnable {
    // invoke secured service
}

executor.execute(originalRunnable)
```

代码执行以下步骤：

* 创建用于我们的`DelegatingSecurityContextExecutor`的`SecurityContext`。注意，在这个示例中，我们只需手工创建`SecurityContext`。然而，无论我们在哪里或如何获得`SecurityContext`都不重要（也就是说，如果我们愿意，我们可以从`SecurityContextHolder`获得它）。

* 创建一个 DelegateExecutor，它负责执行提交的`Runnable`s

* 最后，我们创建一个`DelegatingSecurityContextExecutor`，它负责用`DelegatingSecurityContextRunnable`包装传递到 Execute 方法中的任何 runnable。然后，它将包装好的 Runnable 传递给 DelegateExecutor。在此实例中，对于提交到我们的`DelegatingSecurityContextExecutor`的每个 runnable，将使用相同的`SecurityContext`。如果我们运行的是需要由具有提升权限的用户运行的后台任务，那么这很好。

* 此时，你可能会问自己：“这是如何屏蔽我的代码中的任何安全知识的？”我们不需要在自己的代码中创建`SecurityContext`和`DelegatingSecurityContextExecutor`，而是可以插入一个已经初始化的`DelegatingSecurityContextExecutor`实例。

爪哇

```
@Autowired
private Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor

public void submitRunnable() {
Runnable originalRunnable = new Runnable() {
	public void run() {
	// invoke secured service
	}
};
executor.execute(originalRunnable);
}
```

Kotlin

```
@Autowired
lateinit var executor: Executor // becomes an instance of our DelegatingSecurityContextExecutor

fun submitRunnable() {
    val originalRunnable = Runnable {
        // invoke secured service
    }
    executor.execute(originalRunnable)
}
```

现在我们的代码不知道`SecurityContext`正在传播到`Thread`，然后运行`originalRunnable`，然后清除`SecurityContextHolder`。在本例中，使用相同的用户运行每个线程。如果我们希望在调用`executor.execute(Runnable)`时使用来自`SecurityContextHolder`的用户（即当前登录的用户）来处理`originalRunnable`，该怎么办？这可以通过从我们的`DelegatingSecurityContextExecutor`构造函数中删除`SecurityContext`参数来完成。例如：

爪哇

```
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
	new DelegatingSecurityContextExecutor(delegateExecutor);
```

Kotlin

```
val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = DelegatingSecurityContextExecutor(delegateExecutor)
```

现在，每当执行`executor.execute(Runnable)`时，`SecurityContext`首先由`SecurityContextHolder`获得，然后`SecurityContext`用于创建我们的`DelegatingSecurityContextRunnable`。这意味着我们运行`Runnable`的用户与调用`executor.execute(Runnable)`代码的用户相同。

## Spring 安全并发类

有关 Java Concurrent API 和 Spring 任务抽象的附加集成，请参考 Javadoc。一旦你理解了前面的代码，它们就非常不言自明了。

* `DelegatingSecurityContextCallable`

* `DelegatingSecurityContextExecutor`

* `DelegatingSecurityContextExecutorService`

* `DelegatingSecurityContextRunnable`

* `DelegatingSecurityContextScheduledExecutorService`

* `DelegatingSecurityContextSchedulingTaskExecutor`

* `DelegatingSecurityContextAsyncTaskExecutor`

* `DelegatingSecurityContextTaskExecutor`

* `DelegatingSecurityContextTaskScheduler`