此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

在大多数环境中,安全性是按每个基础存储的。 这意味着当在新的 上完成工作时,将丢失。 Spring Security 提供了一些基础结构来帮助使其更易于管理。 Spring Security 为在多线程环境中使用 Spring Security 提供了低级抽象。 事实上,这就是 Spring Security 与 AsyncContext.start(Runnable)Spring MVC Async Integration 集成的基础。ThreadThreadSecurityContextSpring中文文档

DelegatingSecurityContextRunnable

Spring Security 的并发支持中最基本的构建块之一是 . 它包装一个委托,以使用为委托指定的初始化。 然后它调用委托,确保清除后遗症。 看起来像这样:DelegatingSecurityContextRunnableRunnableSecurityContextHolderSecurityContextRunnableSecurityContextHolderDelegatingSecurityContextRunnableSpring中文文档

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

虽然非常简单,但它可以无缝地从一个转移到另一个。 这一点很重要,因为在大多数情况下,这些行为是以个人为基础的。 例如,您可能已经使用 Spring Security 的 <global-method-security> 支持来保护您的某个服务。 现在,您可以将当前传输到调用安全服务的 以下示例演示如何执行此操作:SecurityContextThreadSecurityContextHolderThreadSecurityContextThreadThreadSpring中文文档

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

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

new Thread(wrappedRunnable).start();

上述代码:Spring中文文档

  • 创建一个调用我们的安全服务。 请注意,它不知道 Spring Security。RunnableSpring中文文档

  • 从 中获取我们希望使用的 并初始化 .SecurityContextSecurityContextHolderDelegatingSecurityContextRunnableSpring中文文档

  • 使用 创建 .DelegatingSecurityContextRunnableThreadSpring中文文档

  • 启动我们创建的。ThreadSpring中文文档

由于通常使用 from 创建一个 ,因此它有一个快捷方式构造函数。 以下代码与上述代码具有相同的效果:DelegatingSecurityContextRunnableSecurityContextSecurityContextHolderSpring中文文档

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

DelegatingSecurityContextRunnable wrappedRunnable =
	new DelegatingSecurityContextRunnable(originalRunnable);

new Thread(wrappedRunnable).start();

我们拥有的代码使用起来很简单,但它仍然需要知道我们正在使用 Spring Security。 在下一节中,我们将看看如何利用来隐藏我们正在使用 Spring Security 的事实。DelegatingSecurityContextExecutorSpring中文文档

DelegatingSecurityContextExecutor

在上一节中,我们发现它很容易使用 ,但它并不理想,因为我们必须了解 Spring Security 才能使用它。 现在我们看看如何保护我们的代码免受我们正在使用 Spring Security 的任何知识的影响。DelegatingSecurityContextRunnableDelegatingSecurityContextExecutorSpring中文文档

的设计与 的设计类似,只不过它接受委托而不是委托。 以下示例演示如何使用它:DelegatingSecurityContextExecutorDelegatingSecurityContextRunnableExecutorRunnableSpring中文文档

SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
	UsernamePasswordAuthenticationToken.authenticated("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);

此代码:Spring中文文档

请注意,在此示例中,我们手动创建。 但是,我们在哪里或如何获得它并不重要(例如,我们可以从 获得它)。 * 创建一个负责执行提交对象的对象。 * 最后,我们创建一个 ,它负责用 . 然后,它将包装传递给 . 在这种情况下,每个提交给我们的 . 如果我们运行需要由具有提升权限的用户运行的后台任务,这将很好。 * 在这一点上,你可能会问自己,“这如何保护我的代码对 Spring Security 的任何了解?我们可以注入一个已经初始化的 .SecurityContextSecurityContextSecurityContextHolderdelegateExecutorRunnableDelegatingSecurityContextExecutorRunnableexecuteDelegatingSecurityContextRunnableRunnabledelegateExecutorSecurityContextRunnableDelegatingSecurityContextExecutorSecurityContextDelegatingSecurityContextExecutorDelegatingSecurityContextExecutorSpring中文文档

请看以下示例:Spring中文文档

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

现在我们的代码不知道 正在传播到 ,正在运行,并且 被清除。 在此示例中,使用同一用户来运行每个线程。 如果我们想在调用处理时使用来自的用户(即当前登录的用户)怎么办? 为此,可以从构造函数中删除参数:SecurityContextThreadoriginalRunnableSecurityContextHolderSecurityContextHolderexecutor.execute(Runnable)originalRunnableSecurityContextDelegatingSecurityContextExecutorSpring中文文档

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

现在,任何时候运行,首先由 获得,然后用于创建我们的 . 这意味着我们正在使用用于调用代码的同一用户运行我们的代码。executor.execute(Runnable)SecurityContextSecurityContextHolderSecurityContextDelegatingSecurityContextRunnableRunnableexecutor.execute(Runnable)Spring中文文档