springboot中手机号发送验证码案例

1.这里使用的是RestTemplate调用公司云平台上的短信接口为例
你也可以在网上下载第三方短信平台依赖包,但都是大同小异可参考https://www.cnblogs.com/liabio/p/11718388.html

package com.iflytek.edu.hnezzhxy.controller;

import com.alibaba.fastjson.JSONObject;
import com.iflytek.edu.hnezzhxy.common.config.Constants;
import com.iflytek.edu.hnezzhxy.common.enums.ResponseCodeEnum;
import com.iflytek.edu.hnezzhxy.dao.ZsbmUserDao;
import com.iflytek.edu.hnezzhxy.model.ZsbmUser;
import com.iflytek.edu.hnezzhxy.util.AESUtil;
import com.iflytek.edu.hnezzhxy.util.MD5Util;
import com.iflytek.edu.hnezzhxy.util.ResponseResultUtil;
import com.iflytek.edu.hnezzhxy.util.UUIDUtil;
import com.iflytek.edu.hnezzhxy.vo.ResultVO;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description 登陆处理类
 * @create 2020/06/18 15:44
 * @version 1.0
 */
@RestController
@Validated
@Api(tags = "登陆相关接口")
public class LoginController {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    /** 短信申请接口地址 **/
    @Value(value = "${phone.apiUrl}")
    private String apiUrl;
    /** 产品标识 **/
    @Value(value = "${phone.appId}")
    private String appId;
    /** 短信模板id **/
    @Value(value = "${phone.tId}")
    private String tId;
    /** 短信验证码失效时间 **/
    @Value(value = "${phone.minute}")
    private Integer minute;
    /** 密钥 **/
    @Value(value = "${phone.key}")
    private String key;

    /** 用户接口 **/
    @Autowired
    private ZsbmUserDao zsbmUserDao;

    /** redis模板 **/
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @description 用户登陆接口
     * @param phoneNum 手机号
     * @param passWord 用户密码
     * @param tokens 令牌
     * @param request
     * @return ResultVO
     */
    @RequestMapping("/login")
    @ApiOperation(value="用户登陆接口信息", httpMethod="POST", notes="用户登陆接口信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name="phoneNum",value="手机号",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="passWord",value="密码",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="tokens",value="密码的AES加密方式",paramType="query",dataType="String")
    })
    public ResultVO login(@NotBlank(message = "手机号不能为空!") @Pattern(regexp = "^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$",message = "手机号格式不正确!")
                              @RequestParam(value="phoneNum") String phoneNum,
                          @NotBlank(message = "密码不能为空!")@RequestParam(value="passWord") String passWord,
                          @RequestParam(value="tokens", required = false, defaultValue = "6ay4dlwd4enjbf90") String tokens, HttpServletRequest request) {
        HttpSession session = request.getSession();
        //由于前端穿过来的是=AES加密方式,需要解密然后再通过md5加密去查询
        String pass =this.aesDecrypt(passWord,tokens);
        if(StringUtils.isEmpty(pass)){
            logger.error("AES解密失败!");
            return new ResponseResultUtil().success(ResponseCodeEnum.AES_DEC_FAIL.getCode(),
                    ResponseCodeEnum.AES_DEC_FAIL.getMessage(),null,true);
        }
        String md5Pass = MD5Util.string2MD5(pass);
        ZsbmUser user = zsbmUserDao.selectUserByUserNameAndPassWord(phoneNum, md5Pass);
        if(user!=null){
            session.setAttribute(Constants.SESSION_USER_Attribute,user);
            logger.info("用户信息!"+user);
            logger.info("登陆成功!");
            return new ResultVO(ResponseCodeEnum.LOGIN_SUCCESS.getCode(),ResponseCodeEnum.LOGIN_SUCCESS.getMessage(),user,true);
        }else{
            logger.error("用户名或密码错误!");
            return new ResponseResultUtil().success(ResponseCodeEnum.UNLOGIN_ERROR.getCode(),
                    ResponseCodeEnum.UNLOGIN_ERROR.getMessage(),null,true);
        }
    }


    /**
     * @description 短信发送业务逻辑
     * @param phoneNum 电话号码
     * @param flag true 代表密码找回 false 代表注册
     * @return ResultVO
     */
    @RequestMapping("/sendPost")
    @ApiOperation(value="短信注册或密码找回验证码发送",httpMethod ="POST", notes="短信注册或密码找回验证码发送")
    @ApiImplicitParams({
            @ApiImplicitParam(name="phoneNum",value="手机号",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="flag",value="注册或者密码找回标识:true为密码找回,false注册,不传默认false",paramType="query",dataType="Boolean")
    })
    public ResultVO  sendPost(@NotBlank(message = "手机号不能为空!") @Pattern(regexp = "^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$",message = "手机号格式不正确!")
                              @RequestParam(value="phoneNum") String phoneNum,
                              @RequestParam(value="flag",required = false,defaultValue = "false") Boolean flag){
            ResultVO resultVO=new ResultVO(null,null,null,true);
            String phone=zsbmUserDao.selectPhoneIsExist(phoneNum);
            //true 代表是密码找回  false是注册
            if(flag){
                if(StringUtils.isEmpty(phone)){
                    return new ResponseResultUtil().error(ResponseCodeEnum.PHONE_NO_REGISTER.getCode(),ResponseCodeEnum.PHONE_NO_REGISTER.getMessage());
                }else{
                    this.sendVerificationCode(resultVO,phoneNum);
                }
            }else{
                //判断手机号是否被注册
                if(StringUtils.isEmpty(phone)){
                    this.sendVerificationCode(resultVO,phoneNum);
                }else{
                    resultVO.setCode(ResponseCodeEnum.HAS_REGISTER.getCode());
                    resultVO.setMessage(ResponseCodeEnum.HAS_REGISTER.getMessage());
                }
            }
            return resultVO;
    }

    /**
     * @description 发送验证码
     * @param resultVO
     * @param phoneNum 电话号码
     * @return ResultVO
     */
    private ResultVO sendVerificationCode(ResultVO resultVO,String phoneNum){
        //随机生成固定6位数验证码
        String code = String.valueOf((int)((Math.random()*9+1)*100000));
        StringBuilder sb = new StringBuilder();
        sb.append("appid=").append(appId).append("&phone=").append(phoneNum)
                .append("&tid=").append(tId).append("&tp={Code:'").append(code).append("',Minute:'")
                .append(minute).append("'}&key=").append(key);
        logger.info("拼接后后字符串为" + sb.toString());
        String sign = MD5Util.string2MD5(sb.toString()).toUpperCase();
        logger.info("md5加密后的密钥转大写后为" + sign);
        String[] phones = phoneNum.split(",");
        Map map = new HashMap<>(5);
        Map m = new HashMap<>(2);
        m.put(Constants.VALID_CODE_Attribute, code);
        m.put(Constants.VALID_CODE_MINUTE_Attribute, minute);
        map.put(Constants.VALID_CODE_APPID_Attribute, appId);
        map.put(Constants.VALID_CODE_PHONE_Attribute, phones);
        map.put(Constants.VALID_CODE_TID_Attribute, tId);
        map.put(Constants.VALID_CODE_TP_Attribute, m);
        map.put(Constants.VALID_CODE_SIGN_Attribute, sign);
        logger.info("http Post 请求发送包为" + JSONObject.toJSONString(map));
        String jsonStr = new RestTemplate().postForObject(apiUrl, map, String.class);
        logger.info("短信接收平台返回的结果为json字符串:" + jsonStr);
        if (jsonStr != null ) {
            JSONObject jsonObj =(JSONObject) JSONObject.parse(jsonStr);
            if(jsonObj!=null&&Constants.CLOUD_RETURN_CODE.equals(jsonObj.get(Constants.CLOUD_RETURN_CODE_Attribute))){
                //防止多个用户发送手机验证码,覆盖redis里的key值
                String validCode=new StringBuilder().append(phoneNum).append(":").append(code).toString();
                redisTemplate.opsForValue().set(validCode, code, 5, TimeUnit.MINUTES);

            }else{
                logger.error(ResponseCodeEnum.DX_CLOUD_fAIL.getMessage());
                return new ResponseResultUtil().error(ResponseCodeEnum.DX_CLOUD_fAIL.getCode(),ResponseCodeEnum.DX_CLOUD_fAIL.getMessage());
            }
        }
        resultVO.setCode(ResponseCodeEnum.PHONE_SEND_SUCCESS.getCode());
        resultVO.setMessage(ResponseCodeEnum.PHONE_SEND_SUCCESS.getMessage());
        return resultVO;
    }

    /**
     * @description 用户注册
     * @param phoneNum 手机号
     * @param passWord 密码
     * @param roleId 角色id
     * @param validCode 验证码
     * @return
     */
    @RequestMapping("/register")
    @ApiOperation(value="用户注册",httpMethod ="POST", notes="用户注册")
    @ApiImplicitParams({
            @ApiImplicitParam(name="phoneNum",value="手机号",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="passWord",value="密码",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="roleId",value="角色ID",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="tokens",value="AES加密规则",paramType="query",dataType="String"),
            @ApiImplicitParam(name="validCode",value="6位数短信验证码",required=true,paramType="query",dataType="String")
    })
    public ResultVO  register(@NotBlank(message = "手机号不能为空!") @Pattern(regexp = "^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$",message = "手机号格式不正确")
                              @RequestParam(value = "phoneNum") String phoneNum,
                              @NotBlank(message = "密码不能为空!") @RequestParam(value = "passWord") String passWord,
                              @NotNull(message = "身份不能为空!")   @RequestParam(value = "roleId") Integer roleId,
                              @RequestParam(value="tokens", required = false, defaultValue = "6ay4dlwd4enjbf90") String tokens,
                              @NotNull(message = "验证码不能为空!") @Pattern (regexp = "^\\d{6}$",message = "手机短信验证码必须是6位数!")@RequestParam(value = "validCode") String validCode){
        String vCode=new StringBuilder().append(phoneNum).append(":").append(validCode).toString();
        Object code = redisTemplate.opsForValue().get(vCode);
        if(code!=null){
            String pass =this.aesDecrypt(passWord,tokens);
            if(StringUtils.isEmpty(pass)){
                    logger.error("AES解密失败!");
                    return new ResponseResultUtil().success(ResponseCodeEnum.AES_DEC_FAIL.getCode(),
                            ResponseCodeEnum.AES_DEC_FAIL.getMessage(),null,true);
                }
            if(validCode.equals(code)){
                    String phone = zsbmUserDao.selectPhoneIsExist(phoneNum);
                    if(StringUtils.isEmpty(phone)){
                        zsbmUserDao.addUser(UUIDUtil.uuid(),MD5Util.string2MD5(pass),phoneNum,roleId);
                    }else{
                        return new ResponseResultUtil().success(ResponseCodeEnum.HAS_REGISTER.getCode(),ResponseCodeEnum.HAS_REGISTER.getMessage(),null,true);
                    }
                }else{
                    return new ResponseResultUtil().success(ResponseCodeEnum.VALIDATE_WRITER_ERROR.getCode(),ResponseCodeEnum.VALIDATE_WRITER_ERROR.getMessage(),null,true);
                }
            }else{
                return new ResponseResultUtil().success(ResponseCodeEnum.VALIDATE_EXPIRE.getCode(),ResponseCodeEnum.VALIDATE_EXPIRE.getMessage(),null,true);
            }
            return new ResponseResultUtil().success(ResponseCodeEnum.REGISTER_SUCCESS.getCode(),ResponseCodeEnum.REGISTER_SUCCESS.getMessage(),null,true);
    }

    /**
     * @description 密码找回
     * @param phoneNum 手机号
     * @param passWord 新密码
     * @param validCode 验证码
     * @return ResultVO
     */
    @RequestMapping("/retrievePass")
    @ApiOperation(value="密码找回",httpMethod ="POST", notes="密码找回")
    @ApiImplicitParams({
            @ApiImplicitParam(name="phoneNum",value="手机号",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="passWord",value="密码",required=true,paramType="query",dataType="String"),
            @ApiImplicitParam(name="tokens",value="AES加密规则",paramType="query",dataType="String"),
            @ApiImplicitParam(name="validCode",value="6位数短信验证码",required=true,paramType="query",dataType="String")
    })
    public ResultVO  retrievePass( @NotBlank(message = "手机号不能为空!") @Pattern(regexp = "^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$",message = "手机号格式不正确!")
                                   @RequestParam(value = "phoneNum") String phoneNum,
                                   @NotBlank(message = "密码不能为空!") @RequestParam(value = "passWord") String passWord,
                                   @RequestParam(value="tokens", required = false, defaultValue = "6ay4dlwd4enjbf90") String tokens,
                                   @NotNull(message = "验证码不能为空!") @Pattern (regexp = "^\\d{6}$",message = "手机短信验证码必须是6位数!") @RequestParam(value = "validCode") String validCode) {
        String vCode=new StringBuilder().append(phoneNum).append(":").append(validCode).toString();
        Object code = redisTemplate.opsForValue().get(vCode);
        if(code!=null){
                String pass =this.aesDecrypt(passWord,tokens);
                if(StringUtils.isEmpty(pass)){
                    logger.error("AES解密失败!");
                    return new ResponseResultUtil().success(ResponseCodeEnum.AES_DEC_FAIL.getCode(),
                            ResponseCodeEnum.AES_DEC_FAIL.getMessage(),null,true);
                }
                if(validCode.equals(code)){
                    Timestamp systemdate = new Timestamp(System.currentTimeMillis());
                    zsbmUserDao.updateUserPassWord(phoneNum,MD5Util.string2MD5(pass),systemdate);
                }else{
                    return new ResponseResultUtil().success(ResponseCodeEnum.VALIDATE_WRITER_ERROR.getCode(),ResponseCodeEnum.VALIDATE_WRITER_ERROR.getMessage(),null,true);
                }
            }else{
                return new ResponseResultUtil().success(ResponseCodeEnum.VALIDATE_EXPIRE.getCode(),ResponseCodeEnum.VALIDATE_EXPIRE.getMessage(),null,true);
            }
            return new ResponseResultUtil().success(ResponseCodeEnum.USERPASS_UPDATE_SUCCESS.getCode(),ResponseCodeEnum.USERPASS_UPDATE_SUCCESS.getMessage(),null,true);
    }

    /**
     * @description AES解密
     * @param passWord AES加密的密码
     * @param tokens AES加密规则
     * @return ResultVO
     */
    private String aesDecrypt(String passWord,String tokens){
        try {
            logger.info("AES加密后的密码为:"+passWord);
            logger.info("AES的tokens令牌为:"+tokens);
            return AESUtil.Decrypt(passWord, tokens);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("AES解密失败!");
        }
        return null;
    }

    /**
     * @description 获取登陆状态信息
     * @param session
     * @return ResultVO
     */
    @RequestMapping("/getLoginStatus")
    @ApiOperation(value="获取登陆状态信息",httpMethod ="GET", notes="获取登陆状态信息")
    public ResultVO getLoginStatus(HttpSession session){
        Object object = session.getAttribute(Constants.SESSION_USER_Attribute);
        if(object!=null){
            ZsbmUser zsbmUser=(ZsbmUser)object;
            return  new ResultVO(ResponseCodeEnum.SUCCESS.getCode(),ResponseCodeEnum.SUCCESS.getMessage(),zsbmUser,true);
        }
        return  new ResponseResultUtil().success(ResponseCodeEnum.UNLOGIN_ERROR.getCode(),
                ResponseCodeEnum.UNLOGIN_ERROR.getMessage(),null,true);
    }

    /**
     * @description 退出登录
     * @param session
     * @return ResultVO
     */
    @RequestMapping("/getLoginOut")
    @ApiOperation(value="退出登陆",httpMethod ="GET", notes="退出登陆")
    public ResultVO getLoginOut(HttpSession session){
        Object object = session.getAttribute(Constants.SESSION_USER_Attribute);
        if(object!=null){
                session.removeAttribute(Constants.SESSION_USER_Attribute);
        }
        return  new ResponseResultUtil().success(ResponseCodeEnum.LOGINOUT_SUCCESS.getCode(),
                ResponseCodeEnum.LOGINOUT_SUCCESS.getMessage(),null,true);
    }

}

2.properties或者.yml文件配置

phone:
  apiUrl: https://pretest.xfpaas.com/dripsms/smssafes # 测试环境短信接口申请地址  
  appId: CSW3Y0RD #产品标识
  tId: 12087 #短信模板ID
  minute: 5 #短信验证码几分钟失效
  key: d3c39d38bb59f9428398364fe7a8

3.常量类

package com.iflytek.edu.hnezzhxy.common.config;

/**
 * 常量类
 * @version 1.0
 * @description
 * @create 2020/06/23 19:40
 */
public class Constants {
    /** 短信验证码属性 **/
    public static final String VALID_CODE_Attribute = "Code";
    /** 云平台返回的验证码属性  **/
    public static final String CLOUD_RETURN_CODE_Attribute = "code";
    /** 云平台返回的验证码 **/
    public static final String CLOUD_RETURN_CODE = "000000";
    /** 短信验证码过期属性 **/
    public static final String VALID_CODE_MINUTE_Attribute = "Minute";
    /** 短信产品标识属性 **/
    public static final String VALID_CODE_APPID_Attribute = "appid";
    /** 手机号属性 **/
    public static final String VALID_CODE_PHONE_Attribute = "phone";
    /** 模板id属性 **/
    public static final String VALID_CODE_TID_Attribute = "tid";
    /** 验证码和过期时间的结合属性 **/
    public static final String VALID_CODE_TP_Attribute = "tp";
    /** 密钥属性 **/
    public static final String VALID_CODE_SIGN_Attribute = "sign";
    /** 存在seesion里的用户属性 **/
    public static final String SESSION_USER_Attribute = "user";
    /** 管理员角色 **/
    public static final String ROLE_ADMIN = "admin";
    /** 编码格式 **/
    public static final String ECODE_UTF8="UTF-8";
    /** excel2003 **/
    public static final String EXCEL2003 = "xls";
    /** excel2007 **/
    public static final String EXCEL2007 = "xlsx";
    /** 图像类型信息 **/
    public static final String TYPE_IMAGE= "0";
    /** 成绩 **/
    public static final String TYPE_SCORE= "1";
}

4.响应码枚举类

package com.iflytek.edu.hnezzhxy.common.enums;

/**
 * @description 请求响应状态码和提示信息枚举类
 * @create 2020/06/18 15:44
 * @version 1.0
 */
public enum ResponseCodeEnum {
    /** 请求成功状态码和提示信息 **/
    SUCCESS(200, "success"),
    /** 尚未登录 **/
    UNLOGIN_ERROR(0, "尚未登录!"),
    /** 登陆成功 **/
    LOGIN_SUCCESS(1,"登陆成功!"),
    /** 注册成功 **/
    REGISTER_SUCCESS(2,"注册成功!"),
    /** 验证码过期 **/
    VALIDATE_EXPIRE(3,"验证码过期!"),
    /** 用户密码修改成功 **/
    VALIDATE_WRITER_ERROR(4,"验证码填写错误!"),
    /** 用户密码修改成功 **/
    USERPASS_UPDATE_SUCCESS(5,"用户密码修改成功!"),
    /** 手机号已经被注册 **/
    HAS_REGISTER(6,"手机号已经被注册!"),
    /** 手机号发送验证码成功 **/
    PHONE_SEND_SUCCESS(7,"手机验证码发送成功!"),
    /** 登陆成功 **/
    LOGINOUT_SUCCESS(8,"退出成功!"),
    /** 登陆成功 **/
    IMPORT_SUCCESS(9,"导入成功!"),
    /** 登陆成功 **/
    IMPORT_ERROR(10,"导入失败!"),
    /** 程序报错响应信息 **/
    OPERATE_FAIL(500, "error"),
    /** 短信云平台响应失败 **/
    DX_CLOUD_fAIL(501, "短信云平台响应失败!请联系相关负责人!"),
    /** 该手机号还未被注册,不能找回! **/
    PHONE_NO_REGISTER(502, "该手机号还未被注册,不能找回!"),
    /** AES解密失败 **/
    AES_DEC_FAIL(503, "AES解密失败!请验证解密规则!");
    private Integer code;
    private String message;

    ResponseCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public final Integer getCode() {
        return this.code;
    }

    public final String getMessage() {
        return this.message;
    }
}

你可能感兴趣的:(短信验证码)