OAuth2Exception: Incorrect result size: expected 1, actual 6
问题描述:
多线程并发创建access_token 或者刷新token时,导致oauth_access_token表中,同一个authentication_id出现了多条记录,导致登录时报错OAuth2Exception: Incorrect result size: expected 1, actual 6
maven依赖如下
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
问题原因
oauth2.0在生成token时,源码的表现是:先判断是否存在token,存在删除,然后新增,但是没有同步锁机制,导致多线程并发出现问题。
解决方式一 重写DefaultTokenServices源码:
1.增加接口锁,在创建和刷新token时增加synchronize
public class CustomTokenServices extends DefaultTokenServices {
@Override
public synchronized OAuth2AccessToken createAccessToken( OAuth2Authentication authentication) throws AuthenticationException {
return super.createAccessToken(authentication);
}
@Override
public synchronized OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException {
return super.refreshAccessToken(refreshTokenValue,tokenRequest);
}
}
2.修改配置类,使其生效
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
//增加内容
// OAuth2的主配置信息,启用防止超发token的代码
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.approvalStore(approvalStore());
endpoints.tokenServices(tokenServices(endpoints));
endpoints.authenticationManager(authenticationManager);
endpoints.tokenStore(tokenStore());
endpoints.authorizationCodeServices(authorizationCodeServices());
endpoints.userDetailsService(userDetailsService); //登录的业务逻辑
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //oauth2.0让它支持post也支持get
}
/**
* 重写DefaultTokenServices,添加锁机制,防止超发token
* @param endpoints
* @return
*/
private CustomTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) {
CustomTokenServices service=new CustomTokenServices();
service.setTokenStore(tokenStore());
service.setSupportRefreshToken(true); //支持刷新token
service.setReuseRefreshToken(true);
service.setClientDetailsService(endpoints.getClientDetailsService());
service.setTokenEnhancer(endpoints.getTokenEnhancer());
return service;
}
}
解决方式二 修改依赖,选择没有这个问题的版本
1. maven依赖修改,认证服务器和资源服务器要一致
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.14.RELEASE</version>
</dependency>
2.资源服务器修改
此时,资源服务器的@EnableOAuth2Sso 失效,需要修改下配置文件,使其对接认证服务器生效
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// 省略
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
//重点,使其使用数据库内容,不然认证服务器不会生效
resources.tokenStore(tokenStore());
}
//数据库连接池对象
@Autowired
private DataSource dataSource;
//客户端信息来源
@Bean
public JdbcClientDetailsService jdbcClientDetailsService(){
return new JdbcClientDetailsService(dataSource);
}
//token保存策略
@Bean
public TokenStore tokenStore(){
return new JdbcTokenStore(dataSource);
}
//授权信息保存策略
@Bean
public ApprovalStore approvalStore(){
return new JdbcApprovalStore(dataSource);
}
//授权码模式数据来源
@Bean
public AuthorizationCodeServices authorizationCodeServices(){
return new JdbcAuthorizationCodeServices(dataSource);
}
}