一、注册微信公众号
进入微信公众平台:微信公众平台 (qq.com)注册账号类型选择 订阅号 ,填写信息完成注册
二、开通腾讯云短信服务
搜索短信,免费领取
三、申请短信签名与模版
填写参数,每个参数官方都提供了说明,右侧会根据填写的参数自动生成相关的代码
三、编写发送短信的工具类
@Slf4j
public class SmsUtil {
/**
* 发送短信
*/
public static boolean send(SmsVo smsVo) throws TencentCloudSDKException {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
String secretId = "AKIDkSDpFRPAi2MXVotHXgZMzUlofIhwxkLi";
String secretKey = "FIBqJrOkyzuoZ9Yn170gGpKsFUXSAh7I";
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("sms.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
SmsClient client = new SmsClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
SendSmsRequest req = new SendSmsRequest();
String[] phoneNumberSet1 = {"+86" + smsVo.getPhone()};
req.setPhoneNumberSet(phoneNumberSet1);
req.setSmsSdkAppId("1400829403");
req.setSignName("朝月余晖");
req.setTemplateId(smsVo.getTemplateId());
req.setTemplateParamSet(smsVo.getParam());
// 返回的resp是一个SendSmsResponse的实例,与请求对象对应
SendSmsResponse resp = client.SendSms(req);
//获取响应数据中的Code
SendStatus sendStatus = resp.getSendStatusSet()[0];
String code = sendStatus.getCode();
if("Ok".equals(code)){
return true;
}
//记录错误日志
log.error(sendStatus.getMessage());
return false;
}
四、编写后端发送短信验证码的接口
@ApiOperation("发送验证码")
@GetMapping("/sendCode/{phone}")
public R sendCode(@PathVariable String phone) throws TencentCloudSDKException {
Boolean key = redisTemplate.hasKey(phone);
if (key) {
return R.ok();
}
String code = RandomUtil.getFourBitRandom();
int time = 5;
redisTemplate.opsForValue().set(phone, code, time, TimeUnit.MINUTES);
SmsVo smsVo = new SmsVo();
smsVo.setPhone(phone);
smsVo.setTemplateId("950651"); //短信模版ID
smsVo.setParam(new String[]{code, Integer.toString(time)}); //模版的参数
boolean send = SmsUtil.send(smsVo);
if (send) {
return R.ok();
}
return R.error().message("发送验证码失败!!!");
}
//随机验证码的工具类
public class RandomUtil {
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");
private static final DecimalFormat sixdf = new DecimalFormat("000000");
/**
* 生成4位随机数
*/
public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}
/**
* 生成6位随机数
*/
public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}
}
五、配置redis的序列化方式
redis默认是采用的JDK的IO序列化,这样会出现乱码,所有我们要将其配置为Json格式的序列化方式来解决乱码问题。
@Configuration
public class RedisConfig {
/**
* 设置RedisTemplate规则
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//序列号key value
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
六、登录验证
public Map<String, Object> login(LoginVo loginVo) {
String phone = loginVo.getPhone();
String code = loginVo.getCode();
if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
throw new YyghException(ResultCode.ERROR, "手机号和验证码不能为空");
}
Object codeRedis = redisTemplate.opsForValue().get(phone);
if (!code.equals(codeRedis)) {
throw new YyghException(ResultCode.ERROR, "验证码错误!!!");
}
QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.eq("phone", phone);
UserInfo userInfo = this.getOne(wrapper);
if (!StringUtils.isEmpty(userInfo) && userInfo.getStatus() == 0) {
throw new YyghException(ResultCode.ERROR, "账号被禁用请联系管理员!!!!");
}
//根据openid判断是否是微信扫码登录
String openid = loginVo.getOpenid();
//不是扫码登录
if (StringUtils.isEmpty(openid)) {
//并且手机号不存在
if (userInfo == null) {
userInfo = new UserInfo();
userInfo.setPhone(phone);
this.save(userInfo);
}
} else { //是微信扫码登录
QueryWrapper<UserInfo> userWx = new QueryWrapper<>();
userWx.eq("openid", openid);
UserInfo userInfoWx = this.getOne(userWx);
//根据手机号查询到的用户不存在
if (userInfo == null) {
UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("openid", openid).set("phone", phone);
this.update(updateWrapper);
//将微信账号信息赋值给UserInfo
userInfo = userInfoWx;
} else { //手机号的账号已存在
//手机号已与另一个微信号绑定
if (!StringUtils.isEmpty(userInfo.getOpenid())) {
throw new YyghException(ResultCode.ERROR, "该手机号已绑定");
}
//手机号存在但是没有绑定
userInfo.setOpenid(openid);
userInfo.setNickName(userInfoWx.getNickName());
this.updateById(userInfo);
this.removeById(userInfoWx);
}
}
return UserUtil.getResults(userInfo);
}
public class UserUtil {
public static Map<String, Object> getResults(UserInfo info){
String name = info.getName();
if (StringUtils.isEmpty(name)) {
name = info.getNickName();
}
if (StringUtils.isEmpty(name)) {
name = info.getPhone();
}
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("token", JwtHelper.createToken(info.getId(), name));
return map;
}
}