SpringSecurityFilterチェーンは、インバウンドリクエストスレッドで実行され、別のスレッドで実行される非同期コードに渡されるため、@Asyncリクエストのリクエストごとに2回実行されます。応答スレッドに書き込みを試みると、SpringSecurityFilterチェーンが再び実行されます。async要求によるスプリングセキュリティoauth2の使用
これは、RemoteTokenServicesを使用しているため、access_tokenの有効期限近くに問題が発生しています。元の要求が認証され、サービスアクティビティが約1秒かかると、RemoteTokenServicesが再び呼び出されます。その時点でaccess_tokenは期限切れですそのリクエストは401を返します。
ここで推奨される解決策は何ですか?私は、SecurityFilterChainが応答スレッドで2回目の実行を妨げることができませんでした。間違ったことをやっているのですか?私はSecurityContextが@Asyncスレッドに正しく渡されているのを見ますが、応答スレッドではnullです。
SecurityFilterChainが要求ごとに1回だけ実行されるようにする方法はありますか?または、リクエストごとに複数のフィルタ呼び出しを受け入れ、何とかキャッシュして処理するソリューションですか?
私はspring-boot 1.3.3.RELEASEとspring-security-oauth2 2.0.9.RELEASEを使用しています。
ログ:
INFO [..nio-exec-1] [Caching...] loadAuthentication: 0bc97f92-9ebb-411f-9e8e-e7dc137aeffe
DEBUG [..nio-exec-1] [Caching...] Entering CachingRemoteTokenService auth: null
DEBUG [..nio-exec-1] [Audit...] AuditEvent [timestamp=Wed Mar 30 12:27:45 PDT 2016, principal=testClient, type=AUTHENTICATION_SUCCESS, data={details=remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>}]
INFO [..nio-exec-1] [Controller] Callable testing request received
DEBUG [MvcAsync1] [TaskService] taskBegin
DEBUG [MvcAsync1] [TaskService] Entering TaskService auth: or[email protected]47c78d1a: Principal: testClient; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_CLIENT
DEBUG [MvcAsync1] [TaskService] end of task
INFO [..nio-exec-2] [Caching...] loadAuthentication: 0bc97f92-9ebb-411f-9e8e-e7dc137aeffe
DEBUG [..nio-exec-2] [Caching...] Entering CachingRemoteTokenService auth: null
DEBUG [..nio-exec-2] [RemoteTokenServices] check_token returned error: invalid_token
DEBUG [..nio-exec-2] [Audit...] AuditEvent [timestamp=Wed Mar 30 12:27:47 PDT 2016, principal=access-token, type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.BadCredentialsException, message=0bc97f92-9ebb-411f-9e8e-e7dc137aeffe}]
関連コード:
コントローラ:
@RequestMapping(value = "/callable",
method = RequestMethod.GET,
produces = { MediaType.APPLICATION_JSON_VALUE })
public @ApiResponseObject Callable<ApiResponse> runCallable(HttpServletRequest httpServletRequest)
throws InterruptedException {
log.info(String.format("Callable testing request received"));
Callable<ApiResponse> rv = taskService::execute;
return rv;
}
非同期サービス:
@Override
public ApiResponse execute() {
log.debug("taskBegin");
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering TaskService auth: " + auth);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ApiResponse rv = new ApiResponse();
rv.setStatus(HttpStatus.OK.value());
log.debug("end of task");
return rv;
}
RemoteTokenServices実装(注キャッシングは、Oコメント化されてUT):
public class CachingRemoteTokenService extends RemoteTokenServices {
private static Log log = LogFactory.getLog(CachingRemoteTokenService.class);
@Override
//@Cacheable(cacheNames="tokens", key="#root.methodName + #accessToken")
public OAuth2Authentication loadAuthentication(String accessToken)
throws org.springframework.security.core.AuthenticationException,
InvalidTokenException {
log.info("loadAuthentication: " + accessToken);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering CachingRemoteTokenService auth: " + auth);
return super.loadAuthentication(accessToken);
}
@Override
//@Cacheable(cacheNames="tokens", key="#root.methodName + #accessToken")
public OAuth2AccessToken readAccessToken(String accessToken) {
log.info("readAccessToken: " + accessToken);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
log.debug("Entering CachingRemoteTokenService auth: " + auth);
return super.readAccessToken(accessToken);
}
}
、最終的に私のセキュリティ設定:
@Configuration
public class Oauth2ResourceConfig {
private static Log log = LogFactory.getLog(Oauth2ResourceConfig.class);
@Value("${client.secret}")
private String clientSecret;
@Value("${check.token.endpoint}")
private String checkTokenEndpoint;
@Bean
@Lazy
public ResourceServerTokenServices tokenService() {
CachingRemoteTokenService tokenServices = new CachingRemoteTokenService();
tokenServices.setClientId("test-service");
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(checkTokenEndpoint);
return tokenServices;
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/health-check").permitAll()
.antMatchers("/**").access("#oauth2.isClient() and #oauth2.hasScope('trust')");
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("test-service");
}
}
}