阿里云短信使用很简单
首先我们先引入jar包
<!-- 阿里云短信 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.3</version>
</dependency>
然后我们再加一个工具类用来发送验证码
package com.sakura.user.tool;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class AliyunSmsUtils {
@Value("${aliyun.sms.access-key-id}")
String ALIYUN_SMS_ACCESS_KEY_ID;
@Value("${aliyun.sms.secret}")
String ALIYUN_SMS_SECRET;
@Value("${aliyun.sms.region-id}")
String ALIYUN_SMS_REGION_ID;
@Value("${aliyun.sms.sys-domain}")
String ALIYUN_SMS_SYS_DOMAIN;
@Value("${aliyun.sms.sys-action}")
String ALIYUN_SMS_SYS_ACTION;
@Value("${aliyun.sms.sys-version}")
String ALIYUN_SMS_SYS_VERSION;
public boolean sendSms(String phone, String paramJson, String signName, String templateCode) {
DefaultProfile profile = DefaultProfile.getProfile(ALIYUN_SMS_REGION_ID, ALIYUN_SMS_ACCESS_KEY_ID, ALIYUN_SMS_SECRET);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain(ALIYUN_SMS_SYS_DOMAIN);
request.setSysVersion(ALIYUN_SMS_SYS_VERSION);
request.setSysAction(ALIYUN_SMS_SYS_ACTION);
request.putQueryParameter("RegionId", ALIYUN_SMS_REGION_ID);
request.putQueryParameter("PhoneNumbers", phone);
request.putQueryParameter("SignName", signName);
request.putQueryParameter("TemplateCode", templateCode);
request.putQueryParameter("TemplateParam", paramJson);
try {
CommonResponse response = client.getCommonResponse(request);
JSONObject json = JSONObject.parseObject(response.getData());
if ("OK".equals(json.get("Message"))) {
return true;
} else {
log.error("阿里云短信发送失败:" + phone + "+" + paramJson);
}
} catch (Exception e) {
log.error("阿里云短信发送异常:" + phone + "+" + paramJson);
e.printStackTrace();
}
return false;
}
}
这里有一些配置参数,这些在阿里云平台都可以拿得到
aliyun:
sms: # 阿里云短信服务参数,申请开通的时候有
access-key-id: LTde4GJggqwQ4hvzrP8feyAf # 这里是假的大家就不要用这个了
secret: ivseKA8iHAtsVderlmL31s54fe78QA # 这里是假的大家就不要用这个了
region-id: cn-hangzhou
sys-domain: dysmsapi.aliyuncs.com
sys-action: SendSms
sys-version: 2017-05-25
send-max-num: 5 # 每天每个手机号发送短信最大次数限制,防止恶意脚本
register: # 用户认证信息模版
sign-name: Sakura-Cloud # 这里是假的大家就不要用这个了
template-code: SMS_202320123 # 这里是假的大家就不要用这个了
template-param: code # 阿里云短信模板参数名 # 这里是假的大家就不要用这个了
然后发送短信验证码接口一定要做好防护,比如加上图片验证码和每天发送次数限制,我们之前就出现过恶意脚本调用我们的短信认证接口疯狂发送短信
下面是短信验证码请求示例
首先是controller方法
package com.sakura.user.controller;
import com.sakura.common.api.ApiResult;
import com.sakura.common.enums.OperationLogType;
import com.sakura.common.log.Module;
import com.sakura.common.log.OperationLog;
import com.sakura.user.param.SMSCodeParam;
import com.sakura.user.service.CaptchaService;
import com.sakura.user.vo.PictureCodeVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author Sakura
* @date 2023/8/14 14:15
*/
@Slf4j
@RestController
@RequestMapping("/captcha")
@Module("user")
@Api(value = "验证码管理", tags = {"验证码管理"})
public class CaptchaController {
@Autowired
private CaptchaService codeService;
/**
* 图片验证码
*/
@GetMapping("getPictureCode")
@OperationLog(name = "获取图片验证码", type = OperationLogType.INFO)
@ApiOperation(value = "获取图片验证码")
public ApiResult<PictureCodeVo> getPictureCode() throws Exception {
PictureCodeVo pictureCodeVo = codeService.getPictureCode();
return ApiResult.ok(pictureCodeVo);
}
/**
* 短信验证码
*/
@PostMapping(value = "/getSMSCode")
@OperationLog(name = "获取短信验证码", type = OperationLogType.INFO)
@ApiOperation(value = "获取短信验证码")
public ApiResult<String> getSMSCode(@Validated @RequestBody SMSCodeParam smsCodeParam) throws Exception {
String msg = codeService.getSMSCode(smsCodeParam);
return ApiResult.ok(msg);
}
}
然后是service方法
package com.sakura.user.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.sakura.common.constant.CommonConstant;
import com.sakura.common.exception.BusinessException;
import com.sakura.common.redis.RedisUtil;
import com.sakura.common.tool.DateUtil;
import com.sakura.common.tool.StringUtil;
import com.sakura.user.captcha.GifCaptcha;
import com.sakura.user.param.SMSCodeParam;
import com.sakura.user.service.CaptchaService;
import com.sakura.user.tool.AliyunSmsUtils;
import com.sakura.user.vo.PictureCodeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* @author Sakura
* @date 2023/8/14 14:25
*/
@Service
@Slf4j
public class CaptchaServiceImpl implements CaptchaService {
@Value("${aliyun.sms.send-max-num}")
Integer ALIYUN_SMS_SEND_MAX_NUM;
@Value("${aliyun.sms.register.sign-name}")
String ALIYUN_SMS_REGISTER_SIGN_NAME;
@Value("${aliyun.sms.register.template-code}")
String ALIYUN_SMS_REGISTER_TEMPLATE_CODE;
@Value("${aliyun.sms.register.template-param}")
String ALIYUN_SMS_REGISTER_TEMPLATE_PARAM;
@Autowired
private RedisUtil redisUtil;
@Autowired
private AliyunSmsUtils aliyunSmsUtils;
@Override
public PictureCodeVo getPictureCode() throws Exception {
// 通过GifCaptcha生成图片验证码
GifCaptcha gifCaptcha = new GifCaptcha(130, 48, 5);
String verCode = gifCaptcha.text().toLowerCase();
PictureCodeVo pictureCodeVo = new PictureCodeVo();
String key = UUID.randomUUID().toString();
pictureCodeVo.setKey(key);
pictureCodeVo.setImage(gifCaptcha.toBase64());
log.info(key + "+++++++++++++" + verCode); // 测试用的,建议删除
// 将验证码放入Redis,有效期5分钟
redisUtil.set(key, verCode, 60 * 5);
return pictureCodeVo;
}
@Override
public String getSMSCode(SMSCodeParam smsCodeParam) throws Exception {
// 校验图片验证码是否正确,防止脚本刷短信
if (!redisUtil.hasKey(smsCodeParam.getKey())) {
throw new BusinessException(500, "图片验证码已过期");
}
String verCode = redisUtil.get(smsCodeParam.getKey()).toString();
if (StringUtil.isEmpty(verCode) || !verCode.equals(smsCodeParam.getPictureCode())) {
throw new BusinessException(500, "图片验证码已过期");
}
// 验证码使用后就删除
redisUtil.del(smsCodeParam.getKey());
// 用户每天发送短信不得超过最大限制数
long smsNum = redisUtil.incr("sms-send-num" + smsCodeParam.getMobile(), 1);
if (smsNum > ALIYUN_SMS_SEND_MAX_NUM) {
throw new BusinessException(500, "当天短信发送数量已达最大");
}
// 设置当前短信记录发送有效期,当前日期到晚上23:59:59
redisUtil.expire(CommonConstant.SMS_SEND_NUM + smsCodeParam.getMobile(), DateUtil.timeToMidnight());
// 发送短信验证码
String smsCode = String.valueOf((int) ((Math.random() * 9 + 1) * 100000)); // 生成一个6位数验证码
log.info(smsCode); // 测试用,建议删除
JSONObject jsonParam = new JSONObject();
jsonParam.put(ALIYUN_SMS_REGISTER_TEMPLATE_PARAM, smsCode);
boolean sendStatus = aliyunSmsUtils.sendSms(smsCodeParam.getMobile(), jsonParam.toJSONString(),
ALIYUN_SMS_REGISTER_SIGN_NAME, ALIYUN_SMS_REGISTER_TEMPLATE_CODE);
if (!sendStatus) {
redisUtil.decr(CommonConstant.SMS_SEND_NUM + smsCodeParam.getMobile(), 1); // 短信发送失败不计算次数
throw new BusinessException(500, "短信发送失败,请联系管理员");
}
// 将短信验证码放入Redis,有效期5分钟
redisUtil.set(CommonConstant.SMS_CODE + smsCodeParam.getMobile(), smsCode, 60 * 5);
return "验证码已发送至手机号:" + smsCodeParam.getMobile() + ",5分钟内有效!";
}
}
图片验证码的返回参数PictureCodeVo
package com.sakura.user.vo;
import com.sakura.common.base.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @author Sakura
* @date 2023/8/14 14:19
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "图片验证码")
public class PictureCodeVo extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("key")
private String key;
@ApiModelProperty("图片验证码 Base64格式")
private String image;
}
还有验证码请求参数SMSCodeParam
package com.sakura.user.param;
import com.sakura.common.base.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
/**
* 用户表
*
* @author Sakura
* @since 2023-08-14
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "短信验证码参数")
public class SMSCodeParam extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("手机号")
@Pattern(regexp = "^$|^((13[0-9])|(15[^4])|(18[0-9])|(17[0-8])|(16[0-8])|(147))\\d{8}$", message = "手机号码格式错误")
private String mobile;
@ApiModelProperty("key")
@NotBlank(message = "key不能为空")
private String key;
@ApiModelProperty("图片验证码")
@NotBlank(message = "图片验证码不能为空")
private String pictureCode;
}
比如我在用户注册的时候需要用到这个验证码
@Override
public boolean register(UserRegisterParam userRegisterParam) throws Exception {
// 先校验短信验证码是否正确
if (!redisUtil.hasKey(CommonConstant.SMS_CODE + userRegisterParam.getMobile())) {
throw new BusinessException(500, "短信验证码已过期");
}
String smsCode = redisUtil.get(CommonConstant.SMS_CODE + userRegisterParam.getMobile()).toString();
if (StringUtil.isEmpty(smsCode) || !smsCode.equals(userRegisterParam.getSmsCode())) {
throw new BusinessException(500, "短信验证码错误");
}
// 验证码使用后就删除
redisUtil.del(CommonConstant.SMS_CODE + userRegisterParam.getMobile());
// 校验当前手机号是否已存在
User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().
eq(User::getMobile, userRegisterParam.getMobile()).
eq(User::getStatus, 1));
if (user != null) {
throw new BusinessException(500, "用户已存在");
}
// 添加用户信息
user = new User();
BeanUtils.copyProperties(userRegisterParam, user);
user.setUserId(StringUtil.random(32)); // 生成用户ID
user.setSalt(SHA256Util.getSHA256Str(UUID.randomUUID().toString())); // 随机生成盐
// 如果用户未设置密码那么初始密码为123456
if (StringUtil.isEmpty(userRegisterParam.getPassword())) {
// 原始密码为123456
// 前端传输时先加密SHA256(123456)
userRegisterParam.setPassword(SHA256Util.getSHA256Str("123456"));
}
// 二次加密
user.setPassword(SHA256Util.getSHA256Str(userRegisterParam.getPassword() + user.getSalt()));
user.setStatus(1);
userMapper.insert(user);
return true;
}
最后生成动态图片验证码的方法在我之前一篇博客有写:Java-生成动态图片验证码
其它的就是一些Redis和String的工具类,大家要是需要建议直接拉我代码:https://github.com/PX1206/Sakura-Cloud
当然有任何不懂的地方也可以直接问我