上一篇:
江景:SpringBoot OAuth2.0 使用短信验证码登录授权zhuanlan.zhihu.com
使用 Redis 缓存服务,将授权服务返回的短信验证码和 access_token 保存到 redis 中。
添加 redis 依赖
org.springframework.bootspring-boot-starter-data-redis
application.yml 文件, 添加 redis 配置
spring:# redis 配置可以不写,默认就是如下redis:database: 0host: localhostport: 6379
修改 MooseAuthorizationServerConfiguration
- 修改 TokenStore 为 RedisTokenStore # 将原来的 数据库存储 token 改为 redis存储
@Resource private RedisConnectionFactory redisConnectionFactory;@Bean
public TokenStore tokenStore() {// 基于 JDBC 实现,令牌保存到数据库//return new JdbcTokenStore(dataSource);// 基于 Redis 实现,令牌保存到 Redisreturn new RedisTokenStore(redisConnectionFactory);
}
启动服务
- 访问 localhost:7000/oauth/token?grant_type=sms_code&client_id=client&client_secret=secret&phone=1537031501&smsCode=9876
- 查看 redis 服务器数据,发现 access_token, refresh_token 已经保存到 redis 服务器中
继续改造
将原来短信验证码验证的逻辑从 redis 中取发送的短信验证码
- 编写发送短信验证码接口
- 将发送的短信验证码保存到 redis 中
@RestController
@RequestMapping(value = "/api/v1/sms")
@Slf4j
public class SmsCodeController {@Resourceprivate SmsCodeSenderService smsCodeSenderService;@PostMapping("/send")public R> sendSmsCode(@Valid SmsCodeVO smsCodeVO, BindingResult result) {return R.ok(smsCodeSenderService.sendSmsCode(smsCodeVO));}
}
发送短信验证码核心逻辑
- 具体可以查看 https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/service/impl/DefaultSmsCodeSenderServiceImpl.java
private void sendAndSaveSmsCode(SmsCodeVO smsCodeVO) {// 生成短信验证码String smsCode = RandomStringUtils.randomNumeric(6);String phoneNumber = smsCodeVO.getPhone();String smsType = smsCodeVO.getType();// reset save sms code to redisString smsCodeKey = String.format(CacheNameConstant.SMS_CODE_KEY, smsType, phoneNumber);// TODO: get sms code not expired return ?SmsCodeDTO smsCodeDTO = (SmsCodeDTO) redisTemplate.opsForValue().get(smsCodeKey);if (ObjectUtils.isNotEmpty(smsCodeDTO) && !smsCodeDTO.getExpired()) {return;}// sms code save dbSmsCodeDO smsCodeDO = new SmsCodeDO();smsCodeDO.setPhone(phoneNumber);smsCodeDO.setType(smsType);smsCodeDO.setCode(smsCode);smsCodeMapper.insertSmsCode(smsCodeDO);smsCodeDTO =new SmsCodeDTO(smsCode, smsType, SecurityConstant.SMS_TIME_OF_TIMEOUT);redisTemplate.opsForValue().set(smsCodeKey, smsCodeDTO, SecurityConstant.SMS_TIME_OF_TIMEOUT, TimeUnit.SECONDS);// mock, 真正发送短信可以使用 阿里云 sms log.info("phone number [{}] send sms code [{}]", phoneNumber, smsCode);}
修改 SmsCodeTokenGranter
- 构造 SmsCodeTokenGranter Bean 时,传递 RedisTemplate,进行操作 redis
- 具体查看
- https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/granter/SmsCodeTokenGranter.java
- https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/configure/MooseAuthorizationServerConfiguration.java
....
private RedisTemplate redisTemplate;public void setRedisTemplate(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}// 客户端提交的验证码String smsCode = parameters.get(CommonConstant.DEFAULT_PARAMETER_NAME_CODE_SMS);if (StringUtils.isBlank(smsCode)) {throw new BusinessException(ResultCode.SMS_CODE_IS_EMPTY);}//sms_loginString smsCodeKey =String.format(CacheNameConstant.SMS_CODE_KEY, "sms_login", phoneNumber);SmsCodeDTO smsCodeDTO = (SmsCodeDTO) redisTemplate.opsForValue().get(smsCodeKey);// 获取服务中保存的用户验证码, 在生成好后放到缓存中if (ObjectUtils.isEmpty(smsCodeDTO) || smsCodeDTO.getExpired()) {throw new BusinessException(ResultCode.SMS_CODE_ERROR);}String smsCodeCached = smsCodeDTO.getCode();if (!StringUtils.equals(smsCode, smsCodeCached)) {throw new BusinessException(ResultCode.SMS_CODE_ERROR);}// 验证通过后从缓存中移除验证码 etc...redisTemplate.expire(smsCodeKey, 0, TimeUnit.SECONDS);// 客户端提交的手机号码AccountDTO accountDTO = accountService.getAccountByPhone(phoneNumber);....
将 /api/v1/sms/send 添加到 anonymousAntPatterns 集合中,不需要授权访问
- 具体查看 anonymousAntPatterns 集合
- https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/configure/MooseResourceServerConfiguration.java
启动服务测试
发送短信
localhost:7000/api/v1/sms/send?phone=1537031501&type=sms_login
输入错误的验证码
输入正确的验证码