|
此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Security 6.5.3! |
并发支持
在大多数环境中,安全性存储在每个Thread基础。 这意味着当在新的Thread这SecurityContext丢失了。Spring Security 提供了一些基础设施来帮助使其更易于管理。Spring Security 提供了用于在多线程环境中使用 Spring Security 的低级抽象。事实上,这就是 Spring Security 构建的集成AsyncContext.start(Runnable)和 Spring MVC 异步集成。
DelegatingSecurityContextRunnable
Spring Security 的并发支持中最基本的构建块之一是DelegatingSecurityContextRunnable. 它包装一个委托Runnable初始化SecurityContextHolder使用指定的SecurityContext为委托。然后调用委托Runnable,确保清除SecurityContextHolder之后。 这DelegatingSecurityContextRunnable看起来像这样:
public void run() {
try {
SecurityContextHolder.setContext(securityContext);
delegate.run();
} finally {
SecurityContextHolder.clearContext();
}
}
虽然非常简单,但它使传输SecurityContext从一个Thread到另一个。这很重要,因为在大多数情况下,SecurityContextHolder作用于每个Thread基础。 例如,您可能已经使用了 Spring Security 的<global-method-security>支持保护您的一项服务。您现在可以转移SecurityContext当前的Thread到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();
前面的代码:
-
创建一个
Runnable调用我们的安全服务。请注意,它不知道 Spring Security。 -
获取
SecurityContext我们希望从SecurityContextHolder并初始化DelegatingSecurityContextRunnable. -
使用
DelegatingSecurityContextRunnable创建Thread. -
启动
Thread我们创造了。
由于创建DelegatingSecurityContextRunnable使用SecurityContext从SecurityContextHolder,有一个快捷方式构造函数。以下代码与前面的代码具有相同的效果:
Runnable originalRunnable = new Runnable() {
public void run() {
// invoke secured service
}
};
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable);
new Thread(wrappedRunnable).start();
我们拥有的代码易于使用,但它仍然需要知道我们正在使用 Spring Security。在下一节中,我们将了解如何利用DelegatingSecurityContextExecutor以隐藏我们正在使用 Spring Security 的事实。
DelegatingSecurityContextExecutor
在上一节中,我们发现使用DelegatingSecurityContextRunnable,但这并不理想,因为我们必须了解 Spring Security 才能使用它。现在我们看看如何DelegatingSecurityContextExecutor可以保护我们的代码免受我们正在使用 Spring Security 的任何信息。
的设计DelegatingSecurityContextExecutor类似于DelegatingSecurityContextRunnable,但它接受委托Executor而不是委托Runnable. 以下示例演示如何使用它:
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负责执行提交的Runnable对象。 * 最后,我们创建一个DelegatingSecurityContextExecutor,负责包装任何Runnable传递给execute方法与DelegatingSecurityContextRunnable. 然后它传递包装的Runnable到delegateExecutor. 在这种情况下,同样SecurityContext用于每个Runnable提交给我们的DelegatingSecurityContextExecutor. 如果我们运行需要由具有提升权限的用户运行的后台任务,这很好。* 此时,您可能会问自己,“这如何保护我的代码对 Spring Security 的任何了解?而不是创建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);
}
现在,我们的代码不知道SecurityContext正在传播到Thread这originalRunnable运行,并且SecurityContextHolder被清除。在此示例中,使用相同的用户来运行每个线程。如果我们想使用SecurityContextHolder(即当前登录的用户)调用时executor.execute(Runnable)处理originalRunnable?
您可以通过删除SecurityContext来自我们的DelegatingSecurityContextExecutor构造 函数:
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
new DelegatingSecurityContextExecutor(delegateExecutor);
现在,任何时候executor.execute(Runnable)运行时,SecurityContext首先由SecurityContextHolder然后那个SecurityContext用于创建我们的DelegatingSecurityContextRunnable.
这意味着我们正在运行我们的Runnable与用于调用executor.execute(Runnable)法典。
Spring Security并发类
有关与 Java 并发 API 和 Spring Task 抽象的其他集成,请参阅 Javadoc。 一旦您理解了前面的代码,它们就不言自明了。