(02)Dubbo微服务实战-短信验证码服务

974 阅读3分钟

短信服务

短信验证码服务主要是用于手机号码+验证码登录,注册,手机号码绑定,修改密码等。该项目把短信验证码服务作为一个公共的服务,实现创建并发送验证码检查验证码两个接口。使用阿里云的SMS服务 dysms.console.aliyun.com/ 实现。

前提准备

在阿里云的SMS服务上注册短信签名和短信模板。

创建阿里云AccessKey

具体实现

阿里云SMS服务使用较为简单,只需调用相应接口并带上参数即可。

    /**
     * 发送短信验证码的具体逻辑
     *
     * @param phone 手机号码
     * @param authCode 验证码
     * @return 发送结果
     */
    private Result<Void> sendSmsAuthCode(String phone, String authCode) {
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou", accessKeyId, accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("SignName", 这里填签名);
        request.putQueryParameter("TemplateCode", 这里填模板编号);
        request.putQueryParameter("TemplateParam", "{\"code\":\"" + authCode + "\"}");
        try {
            client.getCommonResponse(request);
            return Result.success();
        } catch (ClientException e) {
            logger.warn("Send sms auth code fail");
            return Result.fail(ErrorCode.INTERNAL_ERROR, "Send sms auth code fail.");
        }
    }

添加阿里云SMS服务的依赖。

<!-- 阿里云库 -->  
<dependency>  
	<groupId>com.aliyun</groupId>  
	<artifactId>aliyun-java-sdk-core</artifactId>  
</dependency>  

服务封装

为更方便使用验证码功能,对阿里云的SMS服务进行了进一步封装。实现创建并发送验证码检查验证码两个方法。

创建并发送验证码方法实现

这里随机生成6位验证码,发送到手机,并添加验证码到Redis缓存。

    /**
     * 发送短信验证码服务
     * 该服务会把短信验证码进行缓存
     *
     * @param smsAuthCodeDTO 短信验证码对象
     * @return Result<Void> 返回结果若Result.isSuccess()为true表示发送成功,否则发送失败
     */
    @Override
    public Result<Void> createAndSendSmsAuthCode(SmsAuthCodeDTO smsAuthCodeDTO) {
        // 发送短信验证码到手机
        String authCode = AuthCodeUtils.randomAuthCode();
        Result<Void> sendSmsAuthCodeResult = sendSmsAuthCode(smsAuthCodeDTO.getPhone(), authCode);
        if (!sendSmsAuthCodeResult.isSuccess()) {
            return sendSmsAuthCodeResult;
        }

        // 添加短信验证码到缓存
        String redisKey = SMS_AUTH_CODE_REDIS_PREFIX
                + ":" + smsAuthCodeDTO.getSubject() + ":" + smsAuthCodeDTO.getPhone();
        redisTemplate.opsForValue().set(redisKey, authCode, smsAuthCodeDTO.getExpiredTime(), TimeUnit.MINUTES);

        return Result.success();
    }

检查验证码方法实现

这个方法主要是用于校验验证码使用。先从Redis里取出对应的验证码,然后判断验证码是否正确。若通过验证,如果需要删除验证码则删除。

/**
     * 短信验证码检验验证码是否有效的服务
     * 该服务检验成功后,可以清除该验证码,即一个验证码只能使用一次(SmsAuthCodeDTO.delete == true即可)
     *
     * @param smsAuthCodeDTO 短信验证码对象
     * @return Result<Void> 返回结果若Result.isSuccess()为true表示验证成功,否则验证失败
     */
    @Override
    public Result<Void> checkSmsAuthCode(SmsAuthCodeDTO smsAuthCodeDTO) {
        // 从缓存取出验证码
        String redisKey = SMS_AUTH_CODE_REDIS_PREFIX
                + ":" + smsAuthCodeDTO.getSubject() + ":" + smsAuthCodeDTO.getPhone();
        String authCode = (String) redisTemplate.opsForValue().get(redisKey);

        // 验证码不存在
        if (authCode == null) {
            return Result.fail(ErrorCode.INVALID_PARAMETER, "Auth code not exists.");
        }

        // 验证码不正确
        if (!authCode.equals(smsAuthCodeDTO.getAuthCode())) {
            return Result.fail(ErrorCode.INVALID_PARAMETER, "Auth code error.");
        }

        // 验证通过,如果需要删除验证码,则删除
        if (smsAuthCodeDTO.getDelete()) {
            redisTemplate.delete(redisKey);
        }

        return Result.success();
    }

其他代码

AuthCodeUtils

用于生成验证码。

import org.apache.commons.lang3.RandomStringUtils;

/**
 * 描述:验证码工具类,如手机验证码,邮箱验证码
 *
 * @author: xhsf
 * @create: 2020/11/19 16:08
 */
public class AuthCodeUtils {

    /**
     * 随机验证码,格式为6位数字
     * @return 验证码
     */
    public static String randomAuthCode() {
        return RandomStringUtils.randomNumeric(6);
    }

}

SmsAuthCodeDTO

短信验证码的数据传输对象。

/**
 * 描述:短信验证码,用于发送短信验证码时带的信息
 *
 * @author: xhsf
 * @create: 2020/11/19 13:42
 */
public class SmsAuthCodeDTO implements Serializable {
    @NotBlank
    @Phone
    private String phone;

    /**
     * 主题必须是该业务唯一的,不可以产生冲突,否则不准确
     * 用来作为缓存时key的前缀
     * 推荐格式为{服务名}:{具体业务名}
     */
    @NotBlank
    private String subject;

    /**
     * 缓存键的过期时间,单位分钟
     * 推荐5或10分钟
     * 在调用SmsService.createAndSendSmsAuthCode()时需要带上
     */
    @NotNull(groups = SmsService.CreateAndSendSmsAuthCode.class)
    @Positive
    @Max(10)
    private Integer expiredTime;

    /**
     * 短信验证码
     * 在调用SmsService.checkSmsAuthCode()时需要带上
     */
    @NotBlank(groups = SmsService.CheckSmsAuthCode.class)
    @AuthCode
    private String authCode;

    /**
     * 检查成功后是否删除该键
     * 在调用SmsService.checkSmsAuthCode()时需要带上
     */
    @NotNull(groups = SmsService.CheckSmsAuthCode.class)
    private Boolean delete;
    
    // getters and setters
}