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;
}
}