对于最新的稳定版本,请使用 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
看起来像这样:
-
Java
-
Kotlin
public void run() {
try {
SecurityContextHolder.setContext(securityContext);
delegate.run();
} finally {
SecurityContextHolder.clearContext();
}
}
fun run() {
try {
SecurityContextHolder.setContext(securityContext)
delegate.run()
} finally {
SecurityContextHolder.clearContext()
}
}
虽然非常简单,但它可以无缝地将 SecurityContext 从一个线程转移到另一个线程。
这很重要,因为在大多数情况下,SecurityContextHolder 是基于每个线程执行作的。
例如,您可能已经使用 Spring Security 的<global-method-security> 支持来保护您的一项服务。
您现在可以轻松地将SecurityContext
当前的Thread
到Thread
调用安全服务。
下面是如何执行此作的示例:
-
Java
-
Kotlin
Runnable originalRunnable = new Runnable() {
public void run() {
// invoke secured service
}
};
SecurityContext context = SecurityContextHolder.getContext();
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable, context);
new Thread(wrappedRunnable).start();
val originalRunnable = Runnable {
// invoke secured service
}
val context: SecurityContext = SecurityContextHolder.getContext()
val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable, context)
Thread(wrappedRunnable).start()
上面的代码执行以下步骤:
-
创建一个
Runnable
这将调用我们的安全服务。 请注意,它不知道 Spring Security -
获取
SecurityContext
我们希望从SecurityContextHolder
并初始化DelegatingSecurityContextRunnable
-
使用
DelegatingSecurityContextRunnable
创建线程 -
启动我们创建的线程
由于创建DelegatingSecurityContextRunnable
使用SecurityContext
从SecurityContextHolder
它有一个快捷方式构造函数。
以下代码与上面的代码相同:
-
Java
-
Kotlin
Runnable originalRunnable = new Runnable() {
public void run() {
// invoke secured service
}
};
DelegatingSecurityContextRunnable wrappedRunnable =
new DelegatingSecurityContextRunnable(originalRunnable);
new Thread(wrappedRunnable).start();
val originalRunnable = Runnable {
// invoke secured service
}
val wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable)
Thread(wrappedRunnable).start()
我们拥有的代码使用起来很简单,但它仍然需要知道我们正在使用 Spring Security。
在下一节中,我们将了解如何利用DelegatingSecurityContextExecutor
以隐藏我们正在使用 Spring Security 的事实。
DelegatingSecurityContextExecutor
在上一节中,我们发现使用DelegatingSecurityContextRunnable
,但这并不理想,因为我们必须了解 Spring Security 才能使用它。
让我们来看看如何DelegatingSecurityContextExecutor
可以保护我们的代码免受我们正在使用 Spring Security 的任何信息。
的设计DelegatingSecurityContextExecutor
与DelegatingSecurityContextRunnable
除了它接受委托Executor
而不是委托Runnable
.
您可以在下面看到如何使用它的示例:
-
Java
-
Kotlin
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);
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)
该代码执行以下步骤:
-
创建
SecurityContext
用于我们的DelegatingSecurityContextExecutor
. 请注意,在此示例中,我们只是创建SecurityContext
手工。 但是,我们从何处或如何获得SecurityContext
(即我们可以从SecurityContextHolder
如果我们愿意的话)。 -
创建一个负责执行提交的委托执行器
Runnable
s -
最后,我们创建一个
DelegatingSecurityContextExecutor
它负责用DelegatingSecurityContextRunnable
. 然后,它将包装的 Runnable 传递给 delegateExecutor。 在本例中,相同的SecurityContext
将用于提交给我们的每个 RunnableDelegatingSecurityContextExecutor
. 如果我们正在运行需要由具有提升权限的用户运行的后台任务,这很好。 -
在这一点上,您可能会问自己“这如何保护我的代码对 Spring Security 的任何了解?而不是创建
SecurityContext
和DelegatingSecurityContextExecutor
在我们自己的代码中,我们可以注入一个已经初始化的实例DelegatingSecurityContextExecutor
.
-
Java
-
Kotlin
@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);
}
@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
被清除。
在此示例中,使用同一个用户来运行每个线程。
如果我们想使用SecurityContextHolder
当时我们调用了executor.execute(Runnable)
(即当前登录的用户)处理originalRunnable
?
这可以通过删除SecurityContext
来自我们的DelegatingSecurityContextExecutor
构造 函数。
例如:
-
Java
-
Kotlin
SimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();
DelegatingSecurityContextExecutor executor =
new DelegatingSecurityContextExecutor(delegateExecutor);
val delegateExecutor = SimpleAsyncTaskExecutor()
val executor = DelegatingSecurityContextExecutor(delegateExecutor)
现在随时executor.execute(Runnable)
被执行SecurityContext
首先由SecurityContextHolder
然后那个SecurityContext
用于创建我们的DelegatingSecurityContextRunnable
.
这意味着我们正在运行我们的Runnable
与用于调用executor.execute(Runnable)
法典。
Spring Security并发类
有关与 Java 并发 API 和 Spring Task 抽象的其他集成,请参阅 Javadoc。 一旦您理解了前面的代码,它们就非常不言自明。
-
DelegatingSecurityContextCallable
-
DelegatingSecurityContextExecutor
-
DelegatingSecurityContextExecutorService
-
DelegatingSecurityContextRunnable
-
DelegatingSecurityContextScheduledExecutorService
-
DelegatingSecurityContextSchedulingTaskExecutor
-
DelegatingSecurityContextAsyncTaskExecutor
-
DelegatingSecurityContextTaskExecutor
-
DelegatingSecurityContextTaskScheduler