5

正如標題中所述,當同一客戶端同時查詢令牌端點時(兩個進程請求相同客戶端的令牌同時發生問題時間)。在重負載下使用JdbcTokenStore和DefaultTokenServices時出現OAuth2:DuplicateKeyException

在auth服務器的日誌的消息是這樣的:

2016-12-05 19:08:03.313 INFO 31717 --- [nio-9999-exec-5] o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: DuplicateKeyException, PreparedStatementCallback; SQL [insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)]; ERROR: duplicate key value violates unique constraint "oauth_access_token_pkey" 
     Detail: Key (authentication_id)=(4148f592d600ab61affc6fa90bcbf16f) already exists.; nested exception is org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "oauth_access_token_pkey" 
     Detail: Key (authentication_id)=(4148f592d600ab61affc6fa90bcbf16f) already exists. 

我把PostgreSQL表是這樣的:

CREATE TABLE oauth_access_token 
(
    token_id character varying(256), 
    token bytea, 
    authentication_id character varying(256) NOT NULL, 
    user_name character varying(256), 
    client_id character varying(256), 
    authentication bytea, 
    refresh_token character varying(256), 
    CONSTRAINT oauth_access_token_pkey PRIMARY KEY (authentication_id) 
) 

而且我的應用程序看起來像這樣:

@SpringBootApplication 
public class OAuthServTest { 
    public static void main (String[] args) { 
     SpringApplication.run (OAuthServTest.class, args); 
    } 

    @Configuration 
    @EnableAuthorizationServer 
    @EnableTransactionManagement 
    protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter { 

     @Autowired 
     private AuthenticationManager authenticationManager; 

     @Autowired 
     private DataSource   dataSource; 

     @Bean 
     public PasswordEncoder passwordEncoder () { 
     return new BCryptPasswordEncoder (); 
     } 

     @Override 
     public void configure (AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
     endpoints.authenticationManager (authenticationManager); 
     endpoints.tokenServices (tokenServices ()); 
     } 

     @Override 
     public void configure (ClientDetailsServiceConfigurer clients) throws Exception { 
     clients.jdbc (this.dataSource).passwordEncoder (passwordEncoder ()); 
     } 

     @Override 
     public void configure (AuthorizationServerSecurityConfigurer oauthServer) throws Exception { 
     oauthServer.passwordEncoder (passwordEncoder ()); 
     } 

     @Bean 
     public TokenStore tokenStore () { 
     return new JdbcTokenStore (this.dataSource); 
     } 

     @Bean 
     @Primary 
     public AuthorizationServerTokenServices tokenServices () { 
     final DefaultTokenServices defaultTokenServices = new DefaultTokenServices (); 
     defaultTokenServices.setTokenStore (tokenStore ()); 
     return defaultTokenServices; 
     } 

    } 
} 

我的研究總是引導我到this problem。但是這個bug在很久以前就修復了,而且我正在使用最新版本的Spring Boot(v1.4.2)。

我的猜測是我做錯了什麼,DefaultTokenServices中的令牌檢索沒有發生在事務中?

回答

1

問題出在DefaultTokenServices上的競賽條件。

我找到了解決方法。不說它很華麗,但它的工作原理。這個想法是使用AOP建議添加重試邏輯TokenEndpoint

@Aspect 
@Component 
public class TokenEndpointRetryInterceptor { 

    private static final int MAX_RETRIES = 4; 

    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.*(..))") 
    public Object execute (ProceedingJoinPoint aJoinPoint) throws Throwable { 
    int tts = 1000; 
    for (int i=0; i<MAX_RETRIES; i++) { 
     try { 
     return aJoinPoint.proceed(); 
     } catch (DuplicateKeyException e) { 
     Thread.sleep(tts); 
     tts=tts*2; 
     } 
    } 
    throw new IllegalStateException("Could not execute: " + aJoinPoint.getSignature().getName()); 
    } 

} 
+0

你能詳細說明一下嗎?我有同樣的錯誤,爲什麼我需要這個?我沒有在我的彈簧啓動應用程序中:'@Aspect','@ Around'和'ProceedingJoinPoint'。我怎樣才能解決同樣的問題? – BigDong

相關問題