在用户点击登录多次时会出现无法登录认证的情况 后台报错
org.springframework.security.oauth2.common.exceptions.OAuth2Exception: Incorrect result size: expected 1, actual 6
问题是创建Token的时候出现了并发,所导致的 github上也有 相关的讨论
产生问题的代码
DefaultTokenServices createAccessToken的方法没有控制并发所导致的
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean { private int refreshTokenValiditySeconds = 2592000; private int accessTokenValiditySeconds = 43200; private boolean supportRefreshToken = false; private boolean reuseRefreshToken = true; private TokenStore tokenStore; private ClientDetailsService clientDetailsService; private TokenEnhancer accessTokenEnhancer; private AuthenticationManager authenticationManager; public DefaultTokenServices() { } public void afterPropertiesSet() throws Exception { Assert.notNull(this.tokenStore, "tokenStore must be set"); } @Transactional public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication); OAuth2RefreshToken refreshToken = null; if (existingAccessToken != null) { if (!existingAccessToken.isExpired()) { this.tokenStore.storeAccessToken(existingAccessToken, authentication); return existingAccessToken; } if (existingAccessToken.getRefreshToken() != null) { refreshToken = existingAccessToken.getRefreshToken(); this.tokenStore.removeRefreshToken(refreshToken); } this.tokenStore.removeAccessToken(existingAccessToken); } if (refreshToken == null) { refreshToken = this.createRefreshToken(authentication); } else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken)refreshToken; if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { refreshToken = this.createRefreshToken(authentication); } } OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken); this.tokenStore.storeAccessToken(accessToken, authentication); refreshToken = accessToken.getRefreshToken(); if (refreshToken != null) { this.tokenStore.storeRefreshToken(refreshToken, authentication); } return accessToken; } }
TokenStore的实现我选用的是JdbcTokenStore 然后
可以自定义一个CustomTokenServices, 实现createAccessToken方法 ,事务隔离级别设置成序列化.
class CustomTokenServices extends DefaultTokenServices { @Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE) @Override public OAuth2AccessToken createAccessToken( OAuth2Authentication authentication) throws AuthenticationException { return super.createAccessToken(authentication); } }
当然也可以通过synchronized关键字
class CustomTokenServices extends DefaultTokenServices { @Override public synchronized OAuth2AccessToken createAccessToken( OAuth2Authentication authentication) throws AuthenticationException { return super.createAccessToken(authentication); } }
最后选用的方案是
ALTER TABLE oauth_access_token ADD unique (authentication_id);
后记:
OAuth2版本
<dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.0.14.RELEASE</version> </dependency>
JdbcTokenStore 的sql
Drop table if exists oauth_access_token;
create table oauth_access_token (
create_time timestamp default now(),
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
create_time timestamp default now(),
token_id VARCHAR(255),
token BLOB,
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
相关代码:
spring-cloud-oauth2
原文
https://segmentfault.com/a/1190000022059696
本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 记 Spring OAuth2 同时登录产生多个Token