Spring OAuth2: DuplicateKeyException when using JdbcTokenStore and DefaultTokenServices under heavy load

As mentioned in the title, I experience this problem when the same client simultaneously processes the endpoint of the token (two processes requesting a token for the same client at the same time).

The message in the auth server logs is as follows:

2016-12-05 19:08:03.313 INFO 31717 --- [nio-9999-exec-5] ossoprovider.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. 

I am using PostgreSQL with the following table:

 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) ) 

And my application looks like this:

 @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; } } } 

My research always leads me to this problem . But this error was fixed a long time ago, and I am in the latest version of Spring Boot (v1.4.2).

I assume that I am doing something wrong and the token extraction in DefaultTokenServices does not happen in the transaction?

+5
source share
1 answer

The problem is the race condition on DefaultTokenServices .

I found a workaround. Not to say it's great, but it works. The idea is to add retry logic using the AOP tip around 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()); } } 
+1
source

Source: https://habr.com/ru/post/1260831/


All Articles