@Asyncを使用して、Springでメソッドを非同期的に呼び出します。このメソッドは、@ PreAuthorize、Spring SecurityAnnotationでアノテーションが付けられた他のメソッドを呼び出します。承認を機能させるには、SecurityContextHolder
モードをMODE_INHERITABLETHREADLOCAL
に設定して、認証情報が非同期呼び出しに渡されるようにする必要があります。これまでのところ、すべて正常に動作しています。
ただし、別のユーザーとしてログアウトしてログインすると、非同期メソッドでSecurityContextHolderは、ログアウトした古いユーザーの認証情報を保存します。もちろん、不要なAccessDenied
例外が発生します。同期呼び出しではそのような問題はありません。
<task:executor id="executors" pool-size="10"/>
を定義したので、エグゼキュータプールのスレッドが初期化されると、認証情報が上書きされないという問題がありますか?
MODE_INHERITABLETHREADLOCAL
はスレッドプールでは正しく機能しないと思います。
考えられる解決策として、 ThreadPoolTaskExecutor
をサブクラス化し、そのメソッドをオーバーライドしてSecurityContext
を手動で伝播し、<task:executor>
の代わりにそのエグゼキューターを宣言することができます。このような:
public void execute(final Runnable r) {
final Authentication a = SecurityContextHolder.getContext().getAuthentication();
super.execute(new Runnable() {
public void run() {
try {
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
ctx.setAuthentication(a);
SecurityContextHolder.setContext(ctx);
r.run();
} finally {
SecurityContextHolder.clearContext();
}
}
});
}
これは将来の調査が必要なヒントです(私は疲れすぎていますが、誰かがこれが将来の調査に役立つと思うかもしれません):
今日私はつまずいた org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor
参照 GitHub 。
彼は、セキュリティコンテキストを委任して、@Async
呼び出しを「通過」させるように設計されているようです。
この投稿もご覧ください: Spring Security 3.2 M1のハイライト、サーブレット3 APIサポート は強く関連しているようです。
私もその問題に遭遇しました。 DelegatingSecurityContextAsyncTaskExecutor
を使用して、ThreadPoolTaskExecutorを正しく構成することが重要です。また、initialize()メソッドを呼び出すことも重要です。そうしないと、エラーがスローされます。
// define the TaskExecutor as a bean
@Bean("threadPoolTaskExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(1000);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("Async-");
executor.initialize(); // this is important, otherwise an error is thrown
return new DelegatingSecurityContextAsyncTaskExecutor(executor); // use this special TaskExecuter
}
// the method in your business logic which is called async
@Override
@Async("threadPoolTaskExecutor")
public void yourLogic() {
[..]
}
ラルフとオークからの情報を使用して-
@Asyncを標準のタスクエグゼキュータタグで動作させたい場合は、SpringXML設定を次のように設定します。
<task:annotation-driven executor="_importPool"/>
<task:executor id="_importPool" pool-size="5"/>
<bean id="importPool"
class="org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor">
<constructor-arg ref="_importPool"/>
</bean>
次に、@ Asyncメソッドで、使用するプールを指定します
@Async("importPool")
public void run(ImportJob import) {
...
}
これは、@ Asyncメソッドを呼び出すたびに、スレッドプールスレッドが呼び出し元のスレッドと同じセキュリティコンテキストを使用する場合に機能するはずです。
@Ralphの回答に基づいて、Spring
をthreadpooling
でAync event
を達成し、 http://docs.spring.io/autorepo/docs/ spring-security/4.0.0.M1/apidocs/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.html
<bean id="applicationEventMulticaster"
class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor">
<ref bean="delegateSecurityAsyncThreadPool"/>
</property>
</bean>
<bean id="threadsPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
</bean>
<bean id="delegateSecurityAsyncThreadPool"
class="org.springframework.security.task.DelegatingSecurityContextTaskExecutor">
<constructor-arg ref="threadsPool"/>
</bean>
@axtavtからの回答に追加するには、他のメソッドもオーバーライドする必要があります。
@Override
public <T> Future<T> submit(Callable<T> task) {
ExecutorService executor = getThreadPoolExecutor();
final Authentication a = SecurityContextHolder.getContext().getAuthentication();
try {
return executor.submit(new Callable<T>() {
@Override
public T call() throws Exception {
try {
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
ctx.setAuthentication(a);
SecurityContextHolder.setContext(ctx);
return task.call();
} catch (Exception e) {
slf4jLogger.error("error invoking async thread. error details : {}", e);
return null;
} finally {
SecurityContextHolder.clearContext();
}
}
});
} catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}