我正在使用 org.springframework.security.oauth2 库和 MySQL 5.5.58
大多数情况下,它按预期工作,没有任何问题。 但是,似乎当多个请求到“/ oauth / token”在同一时间(每个在不同的服务器节点上)发生时,服务器正在争夺db记录并且存在死锁:
Handling error: DeadlockLoserDataAccessException, PreparedStatementCallback; SQL [delete from oauth_access_token where token_id = ?]; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
还有其他人看过这个问题吗?我该如何避免这种行为?
由于
答案 0 :(得分:0)
此并发问题导致生成2个具有相同token_id的oauth_access_token。 因此,稍后当JdbcTokenStore尝试通过其密钥(token_id)获取oauth_access_token或将其删除时,该操作可能由于以下原因而失败: NonUniqueResultException或DeadlockLoserDataAccessException。
我的解决方案是将oauth_access_token模式更新为对token_id具有唯一约束,以便首先避免不当重复。 请注意,这确实意味着第二个请求将失败(无论如何都是多余的),但是该用户至少有一个且只有一个令牌,并且数据将保持有效且不会损坏。
此外,我将JdbcTokenStore扩展为CustomTokenStore,从而扩展了错误处理和清理范围,以使数据库保持稳定状态。
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
try{
return super.readAuthentication(token.getValue());
}
catch (RuntimeException e){
OAuth2RefreshToken refreshToken = token.getRefreshToken();
removeRefreshToken(refreshToken);
removeAccessToken(token);
logger().error("readAuthentication", e);
throw e;
}
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
logger().info("getAccessToken begin");
try{
return super.getAccessToken(authentication);
}
catch (IncorrectResultSizeDataAccessException e){
logger().error("getAccessToken", e);
safeRemoveAllAccessToken(authentication);
}
catch (RuntimeException e){
logger().error("getAccessToken", e);
}
return null;
}
@Override
public OAuth2Authentication readAuthentication(String token) {
try{
OAuth2Authentication oAuth2Authentication = super.readAuthentication(token);
if(oAuth2Authentication == null){
removeAccessToken(token);
removeRefreshToken(token);
}
return oAuth2Authentication;
}
catch (RuntimeException e){
logger().error("readAuthentication", e);
throw e;
}
}