Spring Security Note-14
Spring Security OAuth核心源码
接下来,通过源码解析的方式,我们去了解如何将之前开发的
用户名密码登录方式
手机短信登录方式
社交帐号登录方式
以上三种自定义的认证方式,嫁接到Spring Security OAuth认证服务器中,并且实现Token的生成存储;
-
步骤略解
/oauth/token的请求会被TokenEndpoint
拦截,通过ClientDetailsService
获取ClientDetails
(第三方相信),并一起封装在TokenRequest
(请求的信息)中;
用这个TokenRequest
会去调用一个TokenGranter
的接口,在这后面,封装的是四种授权方式不同的实现,在这个接口里面,会根据你传入的grant_type具体选择一个Token的生成逻辑;
生成的逻辑,都会产生两个东西OAuth2Request
(是之前两个信息ClientDetails
和TokenRequest
的整合)和Authentication
;
这两个对象组合起来会生成一个叫OAuth2Authentication
的对象,最终产生的OAuth2Authentication
对象这个包含哪个第三方应用,请求哪个用户授权,授权模式,授权参数等所有的信息;
这个OAuth2Authentication
对象会传入AuthorizationServerTokenServices
接口的实现,最终生成一个OAuth2AccessToken
令牌;
TokenStore实现令牌的存取;
TokenEnhancer实现令牌的增强;
-
密码模式
首先进入到TokenEndpoint中,处理/oauth/token的POST请求,拿到的第三方信息,根据第三方信息去创建一个TokenRequest;
拿到TokenRequest之后,去进行一系列的判断,最终会传到TokenGranter的grant当中产生最终的Token;
CompositeTokenGranter
1 | // 四种授权模式 & 刷新令牌的模式根据grant_type判断 |
AbstractTokenGranter
getAccessToken
产生最终的Token;
1 | protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) { |
DefaultTokenServices
1 | public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { |
重构用户名密码登录
我们通过登录请求,到自定义的Filter重新到登录逻辑进行处理,在自定义的AuthenticationSuccessHandler处理之后,再获得OAuth2Request和Authentication,再达到最后的Token的获取;
通过请求参数,获取CilentId,根据ClientId通过ClientDetailsService获取ClientDetails,让ClientDetails和TokenRequest一同封装成OAuth2Request;
最终目标是得到OAuth2Request对象;
-
改造Token的生成
1 | "imoocAuthenticationSuccessHandler") ( |
改造完成,我们还需要在安全配置类中去配置,保护我们的资源安全;
1 |
|
启动项目测试,做一个登陆的请求,携带用户名和密码并携带:authentication(封装clientId和clientSecret)的信息,因为项目的用户名密码的登录请求路径是authentication/form;
重构短信登录
问题
上部分在用户名密码的登录方式中,我们用Token的方式已经重新实现了,但是依然有许多的问题;
短信验证码登录的逻辑,设计到验证码存储的逻辑,
在这之前,我们是通过浏览器去发起请求,然后在服务器中生成,存储在SESSION当中的;
在之后的请求当中,再对验证码进行校验;
-
解决
在APP的架构下,服务器是不存在SESSION的,那么验证码是无处可放的;
解决的思路,我们是在APP发起请求时,在生成和校验验证码逻辑的过程中,加上deviceId(设备ID),根据你的deviceId放到外部存储当中,进行生成和校验;
我们需要重构验证码的相关代码;
1 | public interface ValidateCodeProcessor { |
1 | public interface ValidateCodeRepository { |
在APP项目中的验证码逻辑;
1 |
|
在Browser项目中的验证码逻辑;
1 |
|
1 | public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor { |
测试
向手机:13012345678. 发送短信验证码801409
-
-
-
在工具里发送的验证码登录请求,实际上以上是带上Cookies的HTTP请求,所以我们需要通过一段CMD代码,转化为APP的请求;
1 | curl -X POST \ |
重构社交登录
简化模式
用户直接访问app,在app里面点击QQ或微信的第三方登录,QQ或微信返回openId和accessToken,app用openId换取令牌,然后返回令牌;
封装请求信息TokenOpenIdAuthenticationToken
1 | public class OpenIdAuthenticationToken extends AbstractAuthenticationToken { |
需要一个过滤器,拦截登录请求,然后把信息封装,交到OpenIdAuthenticationToken里面;
-
拦截请求OpenIdAuthenticationFilter
1 | public interface SecurityConstants { |
1 | public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter { |
验证服务器OpenIdAuthenticationProvider
1 | public class OpenIdAuthenticationProvider implements AuthenticationProvider { |
安全配置类OpenIdAuthenticationSecurityConfig
1 |
|
资源服务器配置ImoocResourceServerConfig
将刚刚写好的OpenId安全配置类加入到配置当中;
1 |
|
发送请求,通过OpenId获取信息,成功登录;
标准授权码模式
标准的授权码模式,APP请求QQ或微信去获取授权码;
然后将授权码交给我们自己的第三方client,由第三方client带着授权码去QQ;
或微信申请令牌,然后发放令牌给第三方应用,然后读取用户数据;
然后第三方应用重新生成自己的令牌返回给APP;
在此处换令牌之前获取授权码,停止服务;
?code=061f8yj728JKBJ0DADl72sMEj72f8yjh
&state=344fadca-77e7-47a4-a3cd-7cd988f1953d
1 | public interface SocialAuthenticationFilterPostProcessor { |
1 | public class ImoocSpringSocialConfigurer extends SpringSocialConfigurer { |
1 | 1) ( |
将成功处理器设置进来imoocAuthenticationSuccessHandler
1 |
|
完成返回第三方应用生成的Token给APP;