代码
代码仓库:地址
代码分支:lesson5
简介
在上一篇文章中,我们使用SpringSecurity OAuth2搭建了一套授权服务,对业务系统进行统一授权管理。OAuth提供了四种授权方式:
- 授权码模式(authorization_code)
- 简化模式(implicit)
- 客户端(client_credentials)
- 密码(password)
在实际业务中上述四种模式不能满足所有要求,例如业务系统接入了短信验证码登录方式,需要进行扩展满足业务需求
手机验证码登录
原理分析
SpringSecurity OAuth在使用@EnableAuthorizationServer注解会自动装配TokenEndpoint对象,这个对象会提供一个POST /oauth/token接口,我们以密码授权模式分析调用流程,如下所示:
用户在时间海绵博客发起用户名密码登录请求,时间海绵博客服务器端接收到请求后,调用OAuth协议中的密码授权模式发送请求到OAuth授权服务器,请求信息如下:
client_id:blog
client_secret:blog
grant_type:password
username:admin
password:admin
在ClientCredentialsTokenEndpointFilter过滤器中对客户端信息(client_id和client_secret)进行校验,授权成功后才能访问TokenEndpoint接口(这里要注意,对于OAuth授权服务器来说,过滤链主要完成对客户端信息的校验,用户信息在TokenEndpoint中进行校验,这是因为不同的授权模式关注的用户信息类型不同,需要具体问题具体分析)。
在TokenEndpoint中依据验证的客户端信息以及请求的授权模式进行对比,校验客户端是否有权限进行特定类型授权请求,校验通过后委托给TokenGranter组件进行具体授权模式校验,如上图所示,ResourceOwnerPasswordTokenGranter负责对密码授权模式请求校验,SpringSecurity OAuth还提供了以下实现来校验授权请求:
-
AuthorizationCodeTokenGranter负责校验授权码模式(authorization_code)请求
-
ClientCredentialsTokenGranter负责校验客户端模式(client_credentials)请求
-
ImplicitTokenGranter负责校验简化模式(implicit)请求
- ResourceOwnerPasswordTokenGranter负责校验密码模式(password)请求
TokenGranter校验成功将返回AccessToken信息,后续客户端可以使用AccessToken信息获取到授权用户信息完成对应操作。
通过上述可以知道,如果要扩展实现短信验证码模式,需要自定义实现TokenGranter组件来校验手机验证码授权请求,TokenGranter定义如下所示:
public interface TokenGranter {
对授权类型以及请求参数进行处理,如果成功则返回AccessToken信息
OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest);
}
手机验证码模式代码实现
实现一个继承ToeknGranter接口的类,通过分析已有的TokenGranter子类,我们可以很容易实现,定义一个SmsCodeTokenGranter类,代码实现如下所示:
public class SmsCodeTokenGranter extends AbstractTokenGranter {
/// 授权模式类型,需要与请求字段grant_type的值相等才会进入处理
private static final String GRANT_TYPE = "sms_code";
/// 验证手机与验证码信息是否匹配,这里只是简单的进行匹配处理,判断是否是否为15000000000,验证码是否为:888888
private final PhoneSmsCodeService phoneSmsCodeService;
public SmsCodeTokenGranter(AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory, PhoneSmsCodeService phoneSmsCodeService
) {
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.phoneSmsCodeService = phoneSmsCodeService;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
String mobile = parameters.get("mobile"); // 手机号
String code = parameters.get("code"); // 短信验证码
///对参数进行基础校验
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(code)) {
throw new InvalidGrantException("授权请求参数异常");
}
/// 校验手机验证码是否符合要求
if (!phoneSmsCodeService.checkSmsCode(mobile, code)) {
throw new InvalidGrantException("授权请求参数异常");
}
/// 这里为了简单直接硬编码写入用户信息,通常需要在数据库中取出用户相关信息
List<GrantedAuthority> roles = new ArrayList<>();
roles.add( new SimpleGrantedAuthority("user"));
User user = new User(mobile, "", roles);
/// 授权模式
UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(user, null, roles);
userAuth.setAuthenticated(true);
OAuth2Request storedOAuth2Request = this.getRequestFactory()
.createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
继承AbstractTokenGranter抽象类(该类继承了TokenGranter接口),我们只需要关注核心逻辑"验证手机验证码信息是否正确",校验正确将返回一个OAuth2Authentication对象,这个对象包含了用户信息以及OAuth2请求信息。 完成这一步后,我们需要将SmsCodeTokenGranter装配到TokenEndpoint组件中对sms_code授权类型进行校验处理,配置逻辑如下:
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)/// 用于密码模式验证时需要提供客户端身份验证信息
.reuseRefreshTokens(false);
取出系统中的四种模式
List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
/// 添加手机验证码的授权模式
granters.add(new SmsCodeTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), phoneSmsCodeService));
/// 这是一个组装模式,实现了TokenGranter接口,循环调用List中的TokenGranter组件进行校验处理,直到返回验证成功信息或者是异常信息
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granters);
endpoints.tokenGranter(compositeTokenGranter);
}
上述在AuthorizationServerConfigurerAdapter配置类中进行,具体参见代码。
运行校验
需要注意,这是一个新的授权模式,因此需要先授权客户端拥有手机短信验证模式请求权限,配置客户端的authorizedGrantTypes属性包含sms_code权限(这一步参见代码)。
启动服务,发送手机验证码授权请求POST /oauth/token,请求参数:
client_id:blog
client_secret:blog
grant_type:sms_code
mobile:15000000000
code:888888
返回参数信息:
{
"access_token": "cf4243c1-085e-4f82-b733-03fb38c90a7c",
"token_type": "bearer",
"refresh_token": "01474dd1-c5c5-436b-98b1-8f146cde2391",
"expires_in": 7199,
"scope": "all user"
}
手机短信验证码授权模式验证成功,具体代码参见代码仓库。
总结
- 扩展自定义授权模式,需要继承TokenGranter接口,实现具体校验逻辑
- 将实现的自定义授权TokenGraner类装配到TokenEndpoint组件中
- 特别注意需要将新的授权模式grantType信息授权给指定的客户端,不然客户端无法发送自定义授权模式请求,例如本案例中的sms_code请求
参考文档
- 有来技术博客
联系方式
技术更新换代速度很快,我们无法在有限时间掌握全部知识,但我们可以在他人的基础上进行快速学习,学习也是枯燥无味的,加入我们学习牛人经验:文章来源:https://www.toymoban.com/news/detail-514623.html
点击:加群讨论 文章来源地址https://www.toymoban.com/news/detail-514623.html
到了这里,关于五、SpringSecurity OAuth2扩展手机验证码授权模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!