明确目标
还记得我们刚了解OAuth时说的,它默认只有四种认证模式,那么如果我们想用短信验证或者第三方登录验证的模式,如何自定义授权模式并嫁接到认证服务器中呢?

即我们自己实现认证方式,然后使用Security造好的Token生成存放校验逻辑进行Token管理
为了达到这个目的,我们需要对SpringSecurity OAuth源码进行解析
源码解析
TokenEndPoint
首先,在我们上一章中,获取令牌的方式是向/oauth/token发送post请求,根据我们对springboot的了解,这个是由EndPoint端点控制的,所以我们直接取看TokenEndPoint源码。
1 | public class TokenEndpoint extends AbstractEndpoint { |
看完源码,我们可以发生请求试试,这里我们使用密码模式,因为比较简单。

可以看到,就是我们请求中的信息,我们接着往下看生成Token的源码
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
生成Token
根据grantType找到正确的Token生成器
首先它会进到TokenGranter的实现类CompositeTokenGranter里
1 | public class CompositeTokenGranter implements TokenGranter { |

这里有四种授权模式,和一个RefreshToken的生成器。
密码模式生成器
因为我们是密码模式,所以使用ClientCredentialsTokenGranter这个生成器
1 | public class ClientCredentialsTokenGranter extends AbstractTokenGranter { |
这里也是一个中间处理类,具体生成还要往下走
具体实现类为AbstractTokenGranter
1 | public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { |
这里我们先看getAccessToken()方法,他的实现分为两步
getOAuth2Authentication():先对密码进行校验createAccessToken():然后再创建成功的令牌
然后我们会发现getAccessToken()方法是protected,所以其实实现还是要看他这个子实现类,使用的设计模式是模板模式。
密码认证获取OAuth2Authentication对象
因为我们这里使用的是密码模式,不同的模式其getOAuth2Authentication方法的实现都是不同的,我们先看看ResourceOwnerPasswordTokenGranter的实现
1 |
|
这里认证流程其实我们在最开始学习表单登录的时候已经看过了,就是对密码进行校验,如果成功创建一个授权的Authorization,这里还会将OAuth2Request和Authentication进行封装生成OAuth2Authentication
真正的生成逻辑
接着我们就要看createAccessToken()方法了,实现类为DefaultTokenServices
1 |
|

TokenStore的实现有多种,这里我们还是存在内存中的
最重要的生成Token的代码终于要来了,也是这个类的私有方法
1 | private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { |
看完之后是不是索然无味了,其实就是使用UUID生成的,我们在之前看到返回accessToken的样式时其实就能猜到。
总结流程

从TokenEndpoint开始,我们总结了以上步骤
TokenEndpoint为获取Token的API入口- 通过
ClientDetailsService获取请求中的第三方应用信息ClientDetails,如client-id,client-secret,scope - 然后根据
ClientDetails和请求中的信息(grantType,认证验证码等)组装成TokenRequest对象 - 使用
TokenGranter生成Token,根据不同的grantType使用正确的授权模式生成 - 在生成Token逻辑中,首先会对密码进行验证,验证通过会将
OAuth2Request和Authorization(封装了用户信息)组装成OAuth2Authorization对象 - 最后使用
DefaultTokenServices生成Token,将Token存储,并对Token增强