接着上一篇继续说,上一篇主要的还是连接邮箱和发信测试,这次主要就是对于接口制作和测试了
首先,按照先一篇的接着写
POM-Maven依赖引入 Spring Data Redis以及Pool连接池
具体为什么我不用Jedis,主要是线程安全问题
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--pool连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
application.yml配置(用的properties的配置这里也有)
#yml配置
spring:
redis:
auth: 123456
host: 127.0.0.1
port: 6379
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: 100
min-idle: 0
#properties配置
spring.redis.auth=123456
spring.redis.host=127.0.0.1
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=100
spring.redis.lettuce.pool.min-idle=0
spring.redis.port=6379
编写验证码查找、删除、匹配服务层(虽然是服务层,但我仍然划在工具类中)
如果我们通过邮箱发送验证码,那么肯定要给验证码设置一个有效期,同一个邮箱在同一时间片段只能过有一个短信验证码,如果在同一时间内重复申请没有,但是没有用,为了避免计算开销,我们可以直接返回(当然,你也可以重新生成)。
一般验证码我们实在注册账号的时候用,我们在注册的时候也会判断用户等级(这个一般是交给前端做,但是后端也可以做做【花里胡哨】)
============重点来了=============
我们存储验证码采用redis,使用SpringDataRedis框架
我们在用户安全类中写个 RedisTmplate 类,并且自动装配,redistemplate的具体用法自查,不做解释,这里就只是实现
/**
* Title
*
* @ClassName: UserSafetyUtil
* @Description: 用户安全工具,生成验证码,密码加密等
* @author: Karos
* @date: 2022/10/15 9:32
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Util.SafetyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import java.util.Date;
import java.util.Locale;
@Component
@Scope("singleton")
public class UserSafetyUtil {
//验证码过期时间,我设置的是5分钟
public static long CHECKCODEEXPTIME=1000L*60*5;
//当前验证码状态
public interface CHECKCODE{
int notExist=0;//该邮箱不存在
int OK=1;//匹配成功
int EXPTIME=2;//过期
int ISUSED=3;//被使用
int NoMatch=4;//不匹配
}
@Autowired
private RedisTemplate redisTemplate;
//使用某个邮箱的验证码
public boolean useCheckCodeByMail(String mailAddress){
if (redisTemplate.hasKey(mailAddress)){
redisTemplate.opsForHash().put(mailAddress,"isUsed",1);
return true;
}
return false;
}
//查找并返回邮箱mailAddress的验证码,没有或者过期了返回空,如果过期了,将验证码删除
public String findUsercode(String mailAddress){
if (redisTemplate.opsForHash().hasKey(mailAddress,"checkCode")){
if ((long)redisTemplate.opsForHash().get(mailAddress,"expDate")<new Date().getTime()) {
String code = (String) redisTemplate.opsForHash().get(mailAddress, "checkCode");
return code;
}
redisTemplate.delete(mailAddress);
return null;
}
return null;
}
//删除验证码,这里的str是邮箱
public boolean delCheckCode(String mailAddress){
if (redisTemplate.hasKey(mailAddress)){
redisTemplate.delete(mailAddress);
return true;
}
return false;
}
//验证码匹配
public int matchCheckCode(String mailAddress,String code){
if (redisTemplate.opsForHash().hasKey(mailAddress,"checkCode")){
if ((long)redisTemplate.opsForHash().get(mailAddress,"expDate")<new Date().getTime()) {
return CHECKCODE.EXPTIME;
}
if ((int)redisTemplate.opsForHash().get(mailAddress,"isUsed")!=0){
return CHECKCODE.ISUSED;
}
if (redisTemplate.opsForHash().get(mailAddress,"checkCode").equals(code))return CHECKCODE.OK;
return CHECKCODE.NoMatch;
}
return CHECKCODE.notExist;
}
//通过邮箱生成验证码,存入Redis并返回
public String MakeCodetoDb(String mailAddress){
String code=UserSafetyUtil.touchCheckCode(mailAddress,null);
redisTemplate.opsForHash().put(mailAddress,"checkCode",code);
redisTemplate.opsForHash().put(mailAddress,"expDate",new Date().getTime()+UserSafetyUtil.CHECKCODEEXPTIME);
redisTemplate.opsForHash().put(mailAddress,"isUsed",0);
return code;
}
//通过字符串str生成对应的验证码
public static String touchCheckCode(String str,String key){
if(key==null)key=UserSafetyUtil.getTIMEstr();
String s=StrHex(new StringBuffer(str),new StringBuffer(key));
return getMD5Hex(s.substring(5,10)).substring(5,11).toUpperCase(Locale.ROOT);
}
//字符串加密算法
public static String StrHex(StringBuffer str, StringBuffer key){
str.append(key);
str.insert(0,getMD5Hex(key.toString()));
return getMD5Hex(str.insert(5,"wzl03").toString());
}
//MD5加密
public static String getMD5Hex(String str){
return DigestUtils.md5DigestAsHex(str.getBytes());
}
//通过时间获取加密密钥key
public static String getTIMEstr() {
return new Long((new Date().getTime()>>3|1024|8086)%3000).toString();
}
}
控制层编写–邮箱服务
在后面的项目开发中,我们邮箱服务不一定只用来发送验证码,也有可能拿来做一些消息预警推送或者一些信息推送
所以这里我们做一个控制层
/**
* Title
*
* @ClassName: MailController
* @Description:
* @author: Karos
* @date: 2022/10/17 0:41
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Controller;
import com.karos.td.Common.http.HttpCode;
import com.karos.td.Util.EmailUtil.EmailSend;
import com.karos.td.Util.JsonUtil.JsonRes;
import com.karos.td.Util.SafetyUtil.UserSafetyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController//Controller+RepouseBody
@RequestMapping("/api/mail")
public class MailController {
@Autowired
private EmailSend es;
@Autowired
private UserSafetyUtil usu;
//@PathVariable /xxxx地址内引入var
//@RequestParam ?add=xxxx
@PostMapping
public JsonRes send(@RequestParam("add") String mailAddress){
String code = usu.findUsercode(mailAddress);
if(code!=null){
es.setMessage(mailAddress,"【OK服务】验证码接收","您好,这是您的验证码,请在5分钟内使用,谢谢:【"+code+"】");
return new JsonRes(HttpCode.Success,"success");
}
code=usu.MakeCodetoDb(mailAddress);
es.setMessage(mailAddress,"【OK服务】验证码接收","您好,这是您的验证码,请在5分钟内使用,谢谢:【"+code+"】");
es.send();
return new JsonRes(HttpCode.Success,"success");
}
}
控制层编写–用户层
/**
* Title
*
* @ClassName: UserController
* @Description: 用户Controller层
* @author: Karos
* @date: 2022/10/16 20:26
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Controller;
import com.karos.td.Common.http.HttpCode;
import com.karos.td.Common.safety.PassWordSafetyLevelMatches;
import com.karos.td.Dto.UserDto;
import com.karos.td.Service.impl.UserServiceimpl;
import com.karos.td.Util.JsonUtil.JsonRes;
import com.karos.td.Util.SafetyUtil.UserSafetyUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController//Controller+RepouseBody
@RequestMapping("/api/users")
public class UserController {
/**Restful风格
* 【GET】 /users # 查询用户信息列表
* 【GET】 /users/1001 # 查看某个用户信息
* 【POST】 /users # 新建用户信息
* 【PUT】 /users/1001 # 更新用户信息(全部字段)
* 【PATCH】 /users/1001 # 更新用户信息(部分字段)
* 【DELETE】 /users/1001 # 删除用户信息
*/
@Autowired
private UserServiceimpl usi;
@Autowired
private UserSafetyUtil usu;
@PostMapping
public String Register(@RequestBody @NotNull UserDto user){
//通过正则表达式判断用户密码等级,即使前端做了,后端再做一次,基于第三方sdk开发时减少开发者工作量(假好人)
if (PassWordSafetyLevelMatches.getPassWordLevel(user.getUpassword())<3){
return new JsonRes(HttpCode.Success,"PassWord level is very low!",null).NoData();
}
//判断当前邮箱号是否存在(Dao层我还没写,先把邮箱写好,哈哈)
if (usi.isExsitUserByMail(user.getEmail())==true){
return new JsonRes(HttpCode.Success,"The E-mail Addres is Used,Please Change the E-mail!",null).toString();
}
int state = usu.matchCheckCode(user.getEmail(), user.getCheckcode());
if (state==UserSafetyUtil.CHECKCODE.notExist){
return new JsonRes(HttpCode.Success,"Please Set CheckCode!",null).NoData();
}
if (state==UserSafetyUtil.CHECKCODE.EXPTIME){
return new JsonRes(HttpCode.Success,"The CheckCode is Exp!",null).NoData();
}
if (state==UserSafetyUtil.CHECKCODE.ISUSED){
return new JsonRes(HttpCode.Success,"The CheckCode is Used!Please Reset CheckCode!",null).NoData();
}
if (state==UserSafetyUtil.CHECKCODE.NoMatch){
return new JsonRes(HttpCode.Success,"The CheckCode isn't Match").NoData();
}
usu.useCheckCodeByMail(user.getEmail());//这里不改变问题也不大【手动滑稽】
boolean b = usu.delCheckCode(user.getEmail());
return new JsonRes(HttpCode.Accepted,"Regesiter OK!",user).AllData();
}
}
代码测试:
写完了,我们Run一下
是不是感觉有那味儿了?
10.18 补充 前面是模拟的过期与被使用的情况,不太建议使用,可以看看下面的代码
UserController层
/**
* Title
*
* @ClassName: UserController
* @Description: 用户Controller层
* @author: Karos
* @date: 2022/10/16 20:26
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Controller;
import com.karos.td.Common.http.HttpCode;
import com.karos.td.Common.safety.PassWordSafetyLevelMatches;
import com.karos.td.Dto.UserDto;
import com.karos.td.Service.impl.UserServiceimpl;
import com.karos.td.Util.JsonUtil.JsonRes;
import com.karos.td.Util.SafetyUtil.UserSafetyUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController//Controller+RepouseBody
@RequestMapping("/api/users")
public class UserController {
/**Restful风格
* 【GET】 /users # 查询用户信息列表
* 【GET】 /users/1001 # 查看某个用户信息
* 【POST】 /users # 新建用户信息
* 【PUT】 /users/1001 # 更新用户信息(全部字段)
* 【PATCH】 /users/1001 # 更新用户信息(部分字段)
* 【DELETE】 /users/1001 # 删除用户信息
*/
@Autowired
private UserServiceimpl usi;
@Autowired
private UserSafetyUtil usu;
@PostMapping
public String Register(@RequestBody @NotNull UserDto user){
//通过正则表达式判断用户密码等级,即使前端做了,后端再做一次,基于第三方sdk开发时减少开发者工作量(假好人)
if (PassWordSafetyLevelMatches.getPassWordLevel(user.getUpassword())<3){
return new JsonRes(HttpCode.Success,"PassWord level is very low!",null).NoData();
}
//判断当前邮箱号是否存在
if (usi.isExsitUserByMail(user.getEmail())==true){
return new JsonRes(HttpCode.Success,"The E-mail Addres is Used,Please Change the E-mail!",null).toString();
}
int state = usu.matchCheckCode(user.getEmail(), user.getCheckcode());
if (state==UserSafetyUtil.CHECKCODE.notExist){
return new JsonRes(HttpCode.Success,"Please Set CheckCode!",null).NoData();
}
if (state==UserSafetyUtil.CHECKCODE.NoMatch){
return new JsonRes(HttpCode.Success,"The CheckCode isn't Match").NoData();
}
usu.useCheckCodeByMail(user.getEmail());
return new JsonRes(HttpCode.Accepted,"Regesiter OK!",user).AllData();
}
}
MailController层
/**
* Title
*
* @ClassName: MailController
* @Description:
* @author: Karos
* @date: 2022/10/17 0:41
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Controller;
import com.karos.td.Common.http.HttpCode;
import com.karos.td.Util.EmailUtil.EmailSend;
import com.karos.td.Util.JsonUtil.JsonRes;
import com.karos.td.Util.SafetyUtil.UserSafetyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController//Controller+RepouseBody
@RequestMapping("/api/mail")
public class MailController {
@Autowired
private EmailSend es;
@Autowired
private UserSafetyUtil usu;
//@PathVariable /xxxx地址内引入var
//@RequestParam ?add=xxxx
@PostMapping
public String send(@RequestParam("add") String mailAddress){
String code = usu.findUsercode(mailAddress);
if(code!=null){
es.setMessage(mailAddress,"【OK服务】验证码接收","您好,这是您的验证码,请在5分钟内使用,谢谢:【"+code+"】");
es.send();
return new JsonRes(HttpCode.Success,"success").NoData().toString();
}
code=usu.MakeCodetoDb(mailAddress);
es.setMessage(mailAddress,"【OK服务】验证码接收","您好,这是您的验证码,请在5分钟内使用,谢谢:【"+code+"】");
es.send();
return new JsonRes(HttpCode.Success,"success").NoData().toString();
}
}
UserSafetyUntil
/**
* Title
*
* @ClassName: UserSafetyUtil
* @Description: 用户安全工具,生成验证码,密码加密等
* @author: 巫宗霖
* @date: 2022/10/15 9:32
* @Blog: https://www.wzl1.top/
*/
package com.karos.td.Util.SafetyUtil;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@Component
@Scope("singleton")
public class UserSafetyUtil {
public static long CHECKCODEEXPTIME=1000L*60*5;
public interface CHECKCODE{
int notExist=0;
int OK=1;
int EXPTIME=2;
int ISUSED=3;
int NoMatch=4;
}
@Autowired
private RedisTemplate redisTemplate;
public boolean useCheckCodeByMail(String mailAddress){
if (redisTemplate.hasKey(mailAddress)){
redisTemplate.delete(mailAddress);//使用了就删除
return true;
}
return false;
}
public String findUsercode(String mailAddress){
if (redisTemplate.opsForHash().hasKey(mailAddress,"checkCode")){
String code = (String) redisTemplate.opsForHash().get(mailAddress, "checkCode");
return code;
}
return null;
}
//过期用法,重复造轮子了
@Deprecated
public boolean delCheckCode(String mailAddress){
if (redisTemplate.hasKey(mailAddress)){
redisTemplate.delete(mailAddress);
return true;
}
return false;
}
public int matchCheckCode(String mailAddress,String code){
if (redisTemplate.opsForHash().hasKey(mailAddress,"checkCode")){
if (redisTemplate.opsForHash().get(mailAddress,"checkCode").equals(code))return CHECKCODE.OK;
return CHECKCODE.NoMatch;
}
return CHECKCODE.notExist;
}
public static String touchCheckCode(String mailAddress,String key){
if(key==null)key=UserSafetyUtil.getTIMEstr();
String s=StrHex(new StringBuffer(mailAddress),new StringBuffer(key));
return getMD5Hex(s.substring(5,10)).substring(5,11).toUpperCase(Locale.ROOT);
}
public String MakeCodetoDb(String mailAddress){
String code=UserSafetyUtil.touchCheckCode(mailAddress,null);
redisTemplate.opsForHash().put(mailAddress,"checkCode",code);
redisTemplate.expire(mailAddress,CHECKCODEEXPTIME, TimeUnit.MILLISECONDS);
return code;
}
public static String StrHex(StringBuffer str, StringBuffer key){
str.append(key);
str.insert(0,getMD5Hex(key.toString()));
return getMD5Hex(str.insert(5,"wzl03").toString());
}
public static String getMD5Hex(String str){
return DigestUtils.md5DigestAsHex(str.getBytes());
}
public static String getTIMEstr() {
return new Long((new Date().getTime()>>3|1024|8086)%3000).toString();
}
}