目录

  • 前言
  • 阿里云
  • 依赖aliyun引入
  • 配置信息(YML)
  • 实体类
  • 阿里云配置类
  • 持久层
  • 业务层
  • 接口
  • 实现业务接口放方法
  • 工具类
  • 控制类
  • 测试


前言

在去年我记得有些过一篇关于短信验证的博文但是只是简单的实现,并未以接口的方式去传入,今天在写一个有关于短信验证的记录一下

阿里云

redis做短信验证 redis短信验证码_java


当然在这里也有相关教学,新手可以先去看看,话不多说,直接上过程

依赖aliyun引入

<dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.0.6</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>1.1.0</version>
        </dependency>

配置信息(YML)

#reids配置
redis:
  key:
    prefix:
      authCode: "portal:authCode:"
      orderId: "portal:orderId:"
    expire:
      authCode: 60 # 验证码超期时间
  host: 127.0.0.1 # Redis服务器地址
  database: 1 # Redis数据库索引(默认为0)
  port: 6379 # Redis服务器连接端口
  password: # Redis服务器连接密码(默认为空)
  jedis:
    pool:
      max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
    max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
    max-idle: 8 # 连接池中的最大空闲连接
    min-idle: 0 # 连接池中的最小空闲连接
    timeout: 3000ms # 连接超时时间(毫秒)

实体类

我这里编写了两个实体类,一个手机短信模板实体类,一个数据库存储实体类
短信模板实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author uncletj
 * @Date 2021-04-12 
 * @Version SpringBoot 2.2.2
 * @projectName
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginDto {
    private String mobile;
    private String verifyCode;
    private String username;
}

数据库存储实体类

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author uncletj
 * @Date 2021/3/22 16:37
 * @Version SpringBoot 2.2.2
 * @projectName 短信信息DTO
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NoteDTO {
    /**
     * 电话号码
     */
    private String phone;
    /**
     * json
     */
    private String json;
    /**
     * 模板值
     */
    private String templateCode;
    /**
     * 短信内容
     */
    private String vcode;
}

阿里云配置类

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

/**
 * @Author uncletj
 * @Date 2021-04-12 14:23
 * @Version SpringBoot 2.2.2
 * @projectName 阿里配置信息
 */
public class Message {

    //产品名称:云通信短信API产品,开发者无需替换
    static final String product = "******";
    //产品域名,开发者无需替换
    static final String domain = "******";
    static final String accessKeyId = "******";
    static final String accessKeySecret = "******";

    public static SendSmsResponse sendSms(String phone , String code) throws ClientException {
        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-shenzhen", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-shenzhen", "cn-shenzhen", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(phone);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName("***");
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode("*******");
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        request.setTemplateParam(code);
        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
        return sendSmsResponse;
    }
}

持久层

这个mapper类主要的效果是实现当发送验证码之后数据库相对应的验证码记录表也会有记录值

import com.duodf.uncletj.dto.login.NoteDTO;

/**
 * @Author uncletj
 * @Date 2021-04-12 
 * @Version SpringBoot 2.2.2
 * @projectName
 */
public interface AdminUserMapper {
     //1.前台登录验证码记录
    int getByNoteInsert(NoteDTO noteDTO);
}

业务层

接口

redis业务接口

/**
 * @Author uncletj
 * @Date 2021-04-12
 * @Version SpringBoot 2.2.2
 * @projectName
 */
public interface RedisService {

    /**
     * 存储数据
     */
    void set(String key, String value);

    /**
     * 获取数据
     */
    String get(String key);

    /**
     * 设置超期时间
     */
    boolean expire(String key, long expire);

    /**
     * 删除数据
     */
    void remove(String key);

    /**
     * 自增操作
     * @param delta 自增步长
     */
    Long increment(String key, long delta);

}

短信验证业务接口

import com.duodf.uncletj.dto.login.NoteDTO;

/**
 * @Author uncletj
 * @Date 2021-04-12
 * @Version SpringBoot 2.2.2
 * @projectName
 */
public interface AdminUserService {
    //1.前台登录验证码记录
    int getByNoteInsert(NoteDTO noteDTO);
}

实现业务接口放方法

redis实现类

import com.duodf.uncletj.service.redis.RedisService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @Author uncletj
 * @Date 2021-02-01 15:19
 * @Version SpringBoot 2.2.2
 * @projectName
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void set(String key, String value) {
         stringRedisTemplate.opsForValue().set(key,value);
    }

    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public boolean expire(String key, long expire) {
        return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public void remove(String key) {
        stringRedisTemplate.delete(key);
    }

    @Override
    public Long increment(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key,delta);
    }
}

数据库存储实现类

import com.duodf.uncletj.dto.login.NoteDTO;
import com.duodf.uncletj.mapper.login.AdminUserMapper;
import com.duodf.uncletj.service.login.AdminUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @Author uncletj
 * @Date 2021-04-12 
 * @Version SpringBoot 2.2.2
 * @projectName
 */
@Service
public class AdminUserServiceImpl implements AdminUserService {

    @Resource
    private AdminUserMapper adminUserMapper;

    @Override
    public int getByNoteInsert(NoteDTO noteDTO) {
        return adminUserMapper.getByNoteInsert(noteDTO);
    }

}

工具类

import java.util.Random;

/**
 * @Author uncletj
 * @Date 2021-04-12
 * @Version SpringBoot 2.2.2
 * @projectName 验证码工具类
 */
public class CodeUtil {

    public static final String VERIFY_CODES = "1234567890";

    /**
     * 使用系统默认字符源生成验证码
     * @param verifySize    验证码长度
     * @return
     */
    public static String generateVerifyCode(int verifySize){
        return generateVerifyCode(verifySize, VERIFY_CODES);
    }

    /**
     * 使用指定源生成验证码
     * @param verifySize    验证码长度
     * @param sources   验证码字符源
     * @return
     */
    public static String generateVerifyCode(int verifySize, String sources){
        if(sources == null || sources.length() == 0){
            sources = VERIFY_CODES;
        }
        int codesLen = sources.length();
        Random rand = new Random(System.currentTimeMillis());
        StringBuilder verifyCode = new StringBuilder(verifySize);
        for(int i = 0; i < verifySize; i++){
            verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));
        }
        return verifyCode.toString();
    }
}

控制类

package com.duodf.uncletj.controller.login;

import com.alibaba.fastjson.JSON;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.utils.StringUtils;
import com.duodf.uncletj.config.Message;
import com.duodf.uncletj.dto.login.LoginDto;
import com.duodf.uncletj.dto.login.NoteDTO;
import com.duodf.uncletj.service.login.AdminUserService;
import com.duodf.uncletj.service.redis.RedisService;
import com.duodf.uncletj.utils.CodeUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author uncletj
 * @Date 2021-02-01 17:05
 * @Version SpringBoot 2.2.2
 * @projectName 用户控制层
 */
@RestController
@RequestMapping("/login")
public class LoginController {

    /***
     * 注入redis模版
     */
    @Autowired
    private RedisService redisService;
    private  String tokenId="TOKEN-USER-";

    @Autowired
    private AdminUserService adminUserService;


    /**
     * 发送短信
     */
    @ResponseBody
    @GetMapping(value = "/send/{mobile}")
    @ApiOperation(value = "发送短信")
    public Map<String,Object> smsXxs(@PathVariable("mobile")String mobile, HttpServletRequest request) throws ClientException {
        Map<String,Object> map = new HashMap<>();
        // 验证码长度
        String code = CodeUtil.generateVerifyCode(4);
        //填充验证码
        String TemplateParam = "{\"code\":\""+code+"\"}";
        //传入手机号码及短信模板中的验证码占位符
        SendSmsResponse response = Message.sendSms(mobile,TemplateParam);
        map.put("verifyCode",code);
        map.put("phone",mobile);
        map.put("token",tokenId+mobile);
        request.getSession().setAttribute("CodePhone",map);
        NoteDTO noteDTO = new NoteDTO();
        String json = JSON.toJSONString(code);
        noteDTO.setJson(TemplateParam);
        noteDTO.setVcode(json);
        noteDTO.setPhone(mobile);
        noteDTO.setTemplateCode(tokenId+mobile);
        adminUserService.getByNoteInsert(noteDTO);
        if( response.getCode().equals("OK")) {
            map.put("isOk","OK");
            //验证码绑定手机号并存储到redis
            redisService.set(tokenId + mobile, code);
            //调用reids工具类中存储方法设置超时时间
            redisService.expire(tokenId + mobile,300);
        }
        return map;
    }

    /**
     * 注册验证
     */
    @ResponseBody
    @PostMapping(value = "/regist")
    public Map<String, Object> validateNum(@RequestBody LoginDto loginDto) throws ClientException {
        Map<String,Object> map = new HashMap<>();
        //首先比对验证码是否失效,传入tonkenId返回redis中的value
        String redisauthcode= redisService.get(tokenId+loginDto.getMobile());
        if(StringUtils.isEmpty(redisauthcode)){
            //如果未取到则过期验证码已失效
            map.put("status",404);
            map.put("msg","验证码已过期");
        }else  if(!"".equals(redisauthcode)&&!loginDto.getVerifyCode().equals(redisauthcode)){
            //验证码错误
            map.put("status",500);
            map.put("msg","验证码错误");
        }else{
            //用户注册成功
            map.put("status",200);
            map.put("msg","注册成功");
            redisService.remove(tokenId+loginDto.getMobile());
        }
        return map;
    }
}

测试

首先打开redis

redis做短信验证 redis短信验证码_java_02


使用postman进行测试

redis做短信验证 redis短信验证码_redis做短信验证_03


验证码一旦发出数据库会存储相应的数据

redis做短信验证 redis短信验证码_java_04


等短信发送成功后在去看看redis应该是有一个值就是现在生成的验证码

redis做短信验证 redis短信验证码_验证码_05


get出来的值刚好就是验证码,我先试下去拿一份错的短信码去验证看看返回结果

redis做短信验证 redis短信验证码_验证码_06


现在拿出正确的试试

redis做短信验证 redis短信验证码_验证码_07


注册成功了

redis做短信验证 redis短信验证码_redis做短信验证_08


当然这个时候redis的验证码也是没有了,这是因为注册一般是只需要一次验证码就好,但是登录可以验证多次就直接不需要调用remove方法即可