坚持是一种态度,优秀是一种习惯!
最近一直在倒腾oauth2,oauth2是一种开放授权的协议。具体的原理不在本篇文章叙述,如同学们有兴趣,可以下次详细介绍。本篇主要讲述这两天遇到的获取token的并发问题。
出现的问题
废话少说,上干货。oauth2的一个主要作用是获取token(有四种方式),并发出现的现象是当有同一个client_id,secret并发访问/oauth/token时报
Incorrect result size: expected 1, actual 2 using jdbctokenstore错误。通过debug发现是oauth中的DefaultTokenService中的createAccessToken方法抛出来的异常
上图中的函数跟进去,发现在获取token之前首先会根据client_id去数据库中查询是否token,当数据库中有>1条记录的时候,抛异常。
此时,数据库中却出现了2条记录,why???
原因
createAccessToken方法的大概的逻辑是先删除后插入,数据库中有2条记录,有时候会更多,怀疑是createAccessToken没有做并发控制,当多个线程同时进入createAccessToken方法时会出现多条记录的现象。于是写了个脚本验证自己的推断:
结果发现,如果同时启动10个线程,同一个client_id会出现10天记录(有时候低于10,纯属看运气),验证了我的推断。
解决方案:
问题根源找到了,就得想办法解决。在spring-security-oauth git的issue上发现有好几个人也遇到同样的问题,具体产生多条记录的,有个老外总结如下:
看不懂英文的,可以去Google翻译,但是作为一个程序猿,还是要有阅读技术类文档的能力。
按照老外的说法是,oauth2目前只支持单线程,认为现实的应用场景也只有单线程用户,wtf!!!殊不知还有脚本这玩意。
目前总体的解决方案有如下2种:
1、继承DefaultTokenService 在createAccessToken方法加上sychronized关键字。但是这种方案有个弊端在于,oauth服务器如果是分布式部署还是会出现并发问题
2、继承DefaultTokenService 将createAccessToken事务级别调SERIALIZABLE。这种方案的弊端在于使用的底层数据库得支持事务
欢迎关注我的公众号Porce2018哈。之后继续分享工作中遇到的各种技术问题。