对于最新的稳定版本,请使用 Spring Security 6.4.1spring-doc.cadn.net.cn

并发支持

在大多数环境中,安全性存储在每个Thread基础。 这意味着,当在新的ThreadSecurityContext丢失了。 Spring Security 提供了一些基础结构来帮助使其更易于管理。 Spring Security 为在多线程环境中使用 Spring Security 提供了低级抽象。 事实上,这就是 Spring Security 集成的基础AsyncContext.start(Runnable)Spring MVC 异步集成spring-doc.cadn.net.cn

DelegatingSecurityContextRunnable

Spring Security 的并发支持中最基本的构建块之一是DelegatingSecurityContextRunnable. 它包装一个委托Runnable要初始化SecurityContextHolder使用指定的SecurityContext对于委托。 然后,它会调用委托Runnable,确保清除SecurityContextHolder之后。 这DelegatingSecurityContextRunnable看起来像这样:spring-doc.cadn.net.cn

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

虽然非常简单,但它可以无缝地传输SecurityContext从 1Thread到另一个。 这很重要,因为在大多数情况下,SecurityContextHolder作用于 per-Thread基础。 例如,您可能已经使用了 Spring Security 的<global-method-security>support 保护您的一项服务。 您现在可以将SecurityContext的当前ThreadThread调用受保护的服务。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

由于创建DelegatingSecurityContextRunnable使用SecurityContextSecurityContextHolder,则有一个 shortcut 构造函数。 以下代码与上述代码具有相同的效果:spring-doc.cadn.net.cn

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

DelegatingSecurityContextRunnable wrappedRunnable =
	new DelegatingSecurityContextRunnable(originalRunnable);

new Thread(wrappedRunnable).start();

我们拥有的代码易于使用,但它仍然需要知道我们正在使用 Spring Security。 在下一节中,我们将看看如何利用DelegatingSecurityContextExecutor来隐藏我们正在使用 Spring Security 的事实。spring-doc.cadn.net.cn

DelegatingSecurityContextExecutor

在上一节中,我们发现使用DelegatingSecurityContextRunnable,但这并不理想,因为我们必须了解 Spring Security 才能使用它。 现在我们看看DelegatingSecurityContextExecutor可以保护我们的代码免受我们正在使用 Spring Security 的任何知识的影响。spring-doc.cadn.net.cn

的设计DelegatingSecurityContextExecutorDelegatingSecurityContextRunnable,只不过它接受一个委托Executor而不是委托人Runnable. 以下示例演示如何使用它:spring-doc.cadn.net.cn

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

请注意,在此示例中,我们创建SecurityContext手工。 但是,我们从何处或如何获得SecurityContext(例如,我们可以从SecurityContextHolder). * 创建一个delegateExecutor负责执行 SubmittedRunnable对象。 * 最后,我们创建一个DelegatingSecurityContextExecutor,它负责包装任何Runnableexecute方法替换为DelegatingSecurityContextRunnable. 然后,它将包装的RunnabledelegateExecutor. 在这种情况下,相同的SecurityContext用于每个Runnable提交到我们的DelegatingSecurityContextExecutor. 如果我们运行需要由具有提升权限的用户运行的后台任务,这很好。 * 此时,您可能会问自己,“这如何保护我的代码免受 Spring Security 的任何知识?而不是创建SecurityContextDelegatingSecurityContextExecutor在我们自己的代码中,我们可以注入一个已经初始化的DelegatingSecurityContextExecutor.spring-doc.cadn.net.cn

请考虑以下示例:spring-doc.cadn.net.cn

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

现在,我们的代码不知道SecurityContext正在传播到ThreadoriginalRunnable运行,并且SecurityContextHolder被清除。 在此示例中,使用同一用户运行每个线程。 如果我们想使用SecurityContextHolder(即当前登录的用户)在我们调用executor.execute(Runnable)处理originalRunnable? 您可以通过删除SecurityContext参数DelegatingSecurityContextExecutor构造 函数:spring-doc.cadn.net.cn

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

现在,任何时候executor.execute(Runnable)运行时,SecurityContext首先由SecurityContextHolder然后SecurityContext用于创建我们的DelegatingSecurityContextRunnable. 这意味着我们正在运行我们的Runnable替换为用于调用executor.execute(Runnable)法典。spring-doc.cadn.net.cn