SpringBoot整合图形验证码和邮箱验证码进行注册
一. 图形验证码
1. 引入图形验证码依赖
<!--图形验证码-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
</dependency>
2. 编写生成图形验证码业务代码
@Slf4j
@Api(tags = "通知模块")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/notify")
public class NotifyController {
private final Producer captchaProducer;
private final StringRedisTemplate redisTemplate;
private static final long CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10; // 10 mins
@ApiOperation("获取验证码")
@GetMapping("/captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
// 生成文字验证码
String text = captchaProducer.createText();
log.info("图形验证码:{}", text);
// 存入redis
redisTemplate.opsForValue().set(getCaptchKey(request), text, CAPTCHA_CODE_EXPIRED, TimeUnit.MILLISECONDS);
// 生成图片验证码
BufferedImage image = captchaProducer.createImage(text);
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
ImageIO.write(image, "jpg", outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log.error("图形验证码生成失败", e);
}
}
/** 获取缓存的Key */
private String getCaptchKey(HttpServletRequest request){
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:captcha:" + SecureUtil.md5(ip + userAgent);
log.info("ip:{},userAgent:{},key:{}",ip,userAgent,key);
return key;
}
}
获取IP地址的工具类如下, MD5使用的是
hutool
包里面的加密工具类SecureUtil
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) {
// "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress="";
}
return ipAddress;
}
二. 邮箱验证码
1. 引入邮箱验证码依赖
<!--发送邮件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2. 配置 yml
配置文件
把下面的一些配置改成你自己的
#邮箱服务配置
mail:
host: smtp.163.com #发送邮件服务器
username: 188xxxxxxxx@163.com #发送邮件的邮箱地址
password: HDQTxxxxxxxxxxxx #客户端授权码,不是邮箱密码,网易的是自己设置的
from: 188xxxxxxxx@163.com # 发送邮件的地址,和上面username一致
properties.mail.smtp.starttls.enable: true
properties.mail.smtp.starttls.required: true
properties.mail.smtp.ssl.enable: true
default-encoding: utf-8
3. 编写邮件发送代码
public class MailServiceImpl implements MailService {
@Resource
private JavaMailSender mailSender;
@Value("${spring.mail.from}")
private String from;
@Override
public void sendMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
// 发件人
message.setFrom(from);
// 收件人
message.setTo(to);
// 邮件主题
message.setSubject(subject);
// 邮件内容
message.setText(content);
// 发送邮件
mailSender.send(message);
log.info("邮件发送成功:{}", message);
}
}
4. 发送验证码
其中的
JsonData
为统一响应结果,
BizCodeEnum
为错误码枚举值,后面也会给出
可以换成你自己的,也可以复制我的,具体代码在下面
controller
层初步校验图形验证码是否正确, 正确之后再执行发送邮件操作
/**
* @author Ccoo
* 2024/2/24
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonData {
/**
* 状态码 0 表示成功,1表示处理中,-1表示失败
*/
private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;
/**
* 成功,传入数据
* @return
*/
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}
/**
* 成功,传入数据
* @return
*/
public static JsonData buildSuccess(String msg) {
return new JsonData(0, null, msg);
}
/**
* 成功,传入数据
* @param data
* @return
*/
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}
/**
* 成功,传入数据
* @param data
* @return
*/
public static JsonData buildSuccess(Object data, String msg) {
return new JsonData(0, data, msg);
}
/**
* 失败,传入描述信息
* @param msg
* @return
*/
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}
/**
* 自定义状态码和错误信息
* @param code
* @param msg
* @return
*/
public static JsonData buildCodeAndMsg(int code, String msg) {
return new JsonData(code, null, msg);
}
/**
* 传入枚举,返回信息
* @param codeEnum
* @return
*/
public static JsonData buildResult(BizCodeEnum codeEnum){
return JsonData.buildCodeAndMsg(codeEnum.getCode(),codeEnum.getMessage());
}
}
/**
* @author Ccoo
* 2024/2/24
*/
@Getter
public enum BizCodeEnum {
/**
* 通用操作码
*/
OPS_REPEAT(110001,"重复操作"),
/**
*验证码
*/
CODE_TO_ERROR(240001,"接收号码不合规"),
CODE_LIMITED(240002,"验证码发送过快"),
CODE_ERROR(240003,"验证码错误"),
CODE_CAPTCHA(240101,"图形验证码错误"),
CODE_CAPTCHA_ERROR(240102,"邮箱验证码错误"),
CODE_SEND_SUCCESS(240103,"验证码发送成功"),
/**
* 账号
*/
ACCOUNT_REPEAT(250001,"账号已经存在"),
ACCOUNT_UNREGISTER(250002,"账号不存在"),
ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"),
ACCOUNT_UNLOGIN(250004,"账号未登录");
private final int code;
private final String message;
BizCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
}
@ApiOperation("发送验证码")
@PostMapping("/send_code")
public JsonData sendRegisterCode(SendCodeRequest sendCodeRequest, HttpServletRequest request) {
// 校验图形验证码
String captchaKey = getCaptchKey(request);
String cacheCaptcah = redisTemplate.opsForValue().get(captchaKey);
// 校验验证码是否正确
if(ObjectUtil.isEmpty(sendCodeRequest) || !sendCodeRequest.getCaptcha().equalsIgnoreCase(cacheCaptcah)){
return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_ERROR);
}
// 删除图形验证码
redisTemplate.delete(captchaKey);
// 发送验证码
return notifyService.sendCode(SendCodeEnum.USER_REGISTER, sendCodeRequest.getTo());
}
获取缓存中验证码, 有则判断是否60S内重复发送, 无则拼接验证码并写入缓存
邮箱验证码在缓存中的Key格式为code : 注册类型 : 收信人方式
比如 :code:USER_REGISTER:257xxxxxxx@qq.com
RandomUtil
也是Hutool
工具包里面的
private static final String SUBJECT = "莜莜专属注册验证码";
private static final String CONTENT = "你的验证码是: %s, 有效时间为60秒, 不要告诉别人哦~";
private static final int CODE_EXPIRED = 60 * 1000 * 10; // 10mins
/**
* 发送验证码
* @param sendCodeEnum 验证码类型
* @param to 发送对象
* @return JsonData
*/
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
// 获取缓存中的验证码
String cacheKey = String.format(CacheKey.CHECK_CODE_KEY, sendCodeEnum.name(), to);
String cacheValue = restTemplate.opsForValue().get(cacheKey);
// 如果缓存中有值, 判断是否重复发送
if(StrUtil.isNotBlank(cacheValue)){
long ttl = Long.parseLong(cacheValue.split("_")[1]);
if(System.currentTimeMillis() - ttl < 60 * 1000){
log.info("验证码发送过快, 时间间隔:{}秒", (System.currentTimeMillis() - ttl) / 1000);
return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
}
}
// 拼接验证码 (验证码+时间戳)
String code = RandomUtil.randomNumbers(4);
String value = code + "_" + System.currentTimeMillis();
// 保存验证码到缓存中
restTemplate.opsForValue().set(cacheKey, value, CODE_EXPIRED, TimeUnit.MILLISECONDS);
// 发送邮箱验证码
if(Validator.isEmail(to)){
mailService.sendMail(to, SUBJECT, String.format(CONTENT, code));
return JsonData.buildResult(BizCodeEnum.CODE_SEND_SUCCESS);
}
return JsonData.buildResult(BizCodeEnum.CODE_TO_ERROR);
}
本期内容分享到此结束啦~