2

私が知る限り、SpringSecurityFilter チェーンは @Async リクエストのリクエストごとに 2 回実行されます。これは、インバウンド リクエスト スレッドで実行され、別のスレッドで実行される非同期コードに渡され、レスポンスに書き込もうとするときに渡されるためです。スレッド SpringSecurityFilter チェーンが再度実行されます。

これは、私が 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: org.springframework.security.oauth2.provider.OAuth2Authentication@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 の実装 (キャッシングはコメントアウトされていることに注意してください):

    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");
        }
    }
}
4

1 に答える 1

1

ここで答えを得ました: https://github.com/spring-projects/spring-security-oauth/issues/736

どうやら修正は構成することですsecurity.filter-dispatcher-types=REQUEST, ERROR

于 2016-03-31T15:29:44.143 に答える