SpringBoot OAuth2.0 使用短信验证码登录授权
实现步骤:
- 自定义授权器,继承 AbstractTokenGranter 类;
- 重写 getOAuth2Authentication 函数,在这个函数中,自定义授权认证逻辑;
- 校验手机号 + 短信验证码是否一致
- getOAuth2Authentication, 手机和验证码校验无误, 返回 OAuth2Authentication 授权信息(access_token)
创建 SmsCodeTokenGranter 继承 AbstractTokenGranter
public class SmsCodeTokenGranter extends AbstractTokenGranter {
private static final String SMS_GRANT_TYPE = "sms_code";
private AccountService accountService;
public SmsCodeTokenGranter(AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory) {
super(tokenServices, clientDetailsService, requestFactory, SMS_GRANT_TYPE);
}
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client,
TokenRequest tokenRequest) {
Map<String, String> parameters =
new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
// 客户端提交的手机号码
String phoneNumber = parameters.get(CommonConstant.DEFAULT_PARAMETER_NAME_PHONE);
if (StringUtils.isBlank(phoneNumber)) {
throw new BusinessException(ResultCode.PHONE_NUMBER_IS_EMPTY);
}
// 客户端提交的验证码
String smsCode = parameters.get(CommonConstant.DEFAULT_PARAMETER_NAME_CODE_SMS);
if (!smsCode.equals("9876")) {
throw new BusinessException(ResultCode.SMS_CODE_ERROR);
}
// 客户端提交的手机号码
AccountDTO accountDTO = accountService.getAccountByPhone(phoneNumber);
// TODO: if account not exist , create a new account ??
if (ObjectUtils.isEmpty(accountDTO)) {
throw new BusinessException(ResultCode.PHONE_NOT_EXITS);
}
// TODO: 权限查询 etc...
RoleDTO role = new RoleDTO();
role.setRole("USER");
//accountService.getAccountRole(accountDTO.getAccountId());
// 根据手机号码查询用户 ...
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole()));
UserDetails user = new MooseUserDetails(accountDTO, null, grantedAuthorities);
// 验证用户状态(是否禁用 etc...)
Authentication userAuth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
// org.springframework.security.core.userdetails.UserDetails 接口的, 所以有 user.getAuthorities()
// 当然该参数传null也行
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
OAuth2Request storedOAuth2Request =
getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}
在 MooseAuthorizationServerConfiguration 添加授权类型
/**
* 添加自定义授权类型
*
* @return List<TokenGranter>
*/
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
// endpoints.getTokenGranter() 获取SpringSecurity OAuth2.0 现有的授权类型
List<TokenGranter> granters =
new ArrayList<TokenGranter>(Collections.singletonList(endpoints.getTokenGranter()));
// 构建短信验证授权类型
SmsCodeTokenGranter smsCodeTokenGranter =
new SmsCodeTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory());
smsCodeTokenGranter.setAccountService(accountService);
// 向集合中添加短信授权类型
granters.add(smsCodeTokenGranter);
// 返回所有类型
return new CompositeTokenGranter(granters);
}
在 MooseAuthorizationServerConfiguration configure 添加所有端点信息
/**
* Authorization Server endpoints.
*
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenGranter(tokenGranter(endpoints))
.tokenStore(tokenStore())
.exceptionTranslator(customOAuth2ResponseExceptionTranslator);
// 用于支持密码模式
endpoints.authenticationManager(authenticationManager);
// userDetailsService refresh_token
endpoints.userDetailsService(userDetailsService);
}
启动服务测试
grant_type:sms_code
client_id:client
client_secret:secret
phone:1537031501
smsCode:9876
localhost:7000/oauth/token?grant_type=sms_code&client_id=client&client_secret=secret&phone=1537031501&smsCode=9876
短信验证码输入错误
短信验证码和手机号码都输入正确
携带 access_token 访问接口
关注公众号 「全栈技术部」
,不断学习更多有趣的技术知识。
![](https://gitee.com/shizidada/moose-resource/raw/master/blog/qrcode_430.jpg)