接着上一篇继续说,上一篇主要的还是连接邮箱和发信测试,这次主要就是对于接口制作和测试了
首先,按照先一篇的接着写
SpringBoot 验证码生成+SMTP邮箱服务配置 – Karos (wzl1.top)
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(); | |
} | |
} |
正文完