短信服务
短信验证码服务主要是用于手机号码+验证码登录,注册,手机号码绑定,修改密码等。该项目把短信验证码服务作为一个公共的服务,实现创建并发送验证码和检查验证码两个接口。使用阿里云的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
}