前后端分离实现验证码
实现流程
1、前端登录页面加载时发送请求来请求验证码接口
2、服务器端接收到用户验证码的请求之后,使用工具类来生成验证码(base64)
3、将获取到的 验证码 对应的code 保存到 redis 中并设置该码的有效时间,一般建议使用 UUID 作为key,code 做value 来进行保存。直接将 验证码对应的 base64码 和 对应的UUID 响应给前端
4、前端输入完毕之后,发送登录请求时,将接收到的 UUID 作为参数一起携带到服务器端。
5、服务器端接收端到用户请求之后,根据携带来的UUID 从redis中取出对应的数据,再根据用户输入的code来进行判断是否相对即可
实现步骤:redis这里就不做说明了,直接使用springboot的 jedis 即可
1、引入hutool的验证码依赖
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.7</version>
</dependency>
2、自定义一个用来保存 验证码 信息的 实体类
package com.sany.crane.oa.core.entity;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ImgResult {
@ApiModelProperty("该验证码对应的UUID")
private String imgUUID;
@ApiModelProperty("该验证码对应的base64加密格式")
private String img;
@ApiModelProperty("该验证码对应的具体码")
private String code;
}
3、对应的 验证码 生成工具类
package com.sany.crane.oa.common.util;
import cn.hutool.core.codec.Base64;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;
@Slf4j
@Data
public class CaptchaUtil {
//验证码个数
private int count = 4;
//验证码宽度,且设置每个字的宽度
private int width = count * 50;
//验证码高度
private int height = 50;
//图片验证码key
private String code = "";
//bufferedImage
private BufferedImage bufferedImage;
//测试写入
public static void main(String[] args) {
long startend = System.currentTimeMillis();
CaptchaUtil captchaUtil = new CaptchaUtil();
//默认验证码位数为4,我这里设为5
captchaUtil.setCount(5);
//得到缓冲区
BufferedImage image = captchaUtil.getImage();
//得到真实验证码
String code = captchaUtil.getCode();
long endTime = System.currentTimeMillis();
System.out.println("验证码为:" + code + "\n花费时间为:" + (endTime - startend) + "\n到E盘根目录下看,文件名为11.jpg");
}
public BufferedImage getImage() {
//图片缓冲区
BufferedImage image = new BufferedImage(width, height, 1);
//获得笔
Graphics graphics = image.getGraphics();
//设置初始画笔为白色
graphics.setColor(new Color(255, 255, 254));
//画满整个图,也就是把图片先变为白色
graphics.fillRect(0, 0, width, height);
Random rd = new Random();
//设置字体
Font font = new Font("宋体", Font.PLAIN, 35 + rd.nextInt(10));
graphics.setFont(font);
char[] chars = "qweCRYHrtasdfBxy678934VTGopNUFKuighjklzSXEDLOP12cvbnmQAZWJMI50".toCharArray();
//画验证码
for (int i = 0; i < count; i++) {
String string = "";
string += chars[rd.nextInt(chars.length)] + "";
graphics.setColor(new Color(rd.nextInt(254), rd.nextInt(254), rd.nextInt(254)));
graphics.drawString(string, 55 * i + rd.nextInt(10), 27 + rd.nextInt(15));
code += string;
}
//干扰点
for (int i = 0; i < 25 * count; i++) {
graphics.setFont(new Font("宋体", Font.PLAIN, 15));
String string = ".";
graphics.setColor(new Color(rd.nextInt(255), rd.nextInt(255), rd.nextInt(255)));
graphics.drawString(string, rd.nextInt(width), rd.nextInt(height));
}
//干扰线
for (int i = 0; i < count + count / 2; i++) {
graphics.setFont(new Font("宋体", Font.PLAIN, 10));
graphics.setColor(new Color(rd.nextInt(255), rd.nextInt(255), rd.nextInt(255)));
graphics.drawLine(rd.nextInt(width), rd.nextInt(height), rd.nextInt(width), rd.nextInt(height));
}
//归还笔
graphics.dispose();
//写到流里面需要用到ImageIo
//这里做的测试,在本地测试下是否画的是那回事
/*try {
ImageIO.write(image,"jpg",new FileOutputStream("E:/11.jpg"));
} catch (IOException e) {
e.printStackTrace();
}*/
this.bufferedImage = image;
return image;
}
public static String getBase64(BufferedImage image) {
String base64 = null;
try {
//输出流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(image, "png", stream);
base64 = Base64.encode(stream.toByteArray());
log.info("生成的图片验证码base64:{}", base64);
} catch (IOException e) {
log.error("生成生成的图片验证码base64失败:{}", e.getMessage());
e.printStackTrace();
}
return base64;
}
}
4、前端发送 axios 请求 来获取 验证码
// 获取验证码
getCode() {
// 请求后台验证码
throw axios.get("/captcha/getCaptcha",).then(res => {
if (res.data.code === 0) {
// 给 图片的 src 赋值
this.codeImg = res.data.data.img;
// 给 UUID 赋值
this.imgUUID = res.data.data.imgUUID;
// this.code = res.data.data.code;
console.log(this.code)
} else {
this.$message.error(res.data.msg)
}
}).catch(error => {
alert("errorAjax")
})
},
5、验证码的显示
<img :src="'data:image/gif;base64,'+ codeImg"
alt="验证码获取失败!" title="看不清?换一张!" class="codeImg"
width="80%" @click="getCode()">
6、后端的处理
@ApiOperation(value = "获取验证码")
@GetMapping("/getCaptcha")
@ResponseBody
public Result getCaptcha() {
//画图工具类
CaptchaUtil imageCode = new CaptchaUtil();
// 获取验证码对应的 base64 编码
String base64 = CaptchaUtil.getBase64(imageCode.getImage());
// 获取对应的 验证码 code
String code = imageCode.getCode();
// 生成 UUID
String imgUUID = UUIDUtil.get32UUID();
// 封装 获取的 验证码相关的数据 到 验证码对象中,并响应
ImgResult imgResult = new ImgResult();
imgResult.setImgUUID(imgUUID);
imgResult.setImg(base64);
imgResult.setCode(code);
// 将验证码的信息保存到 redis中,并设置 有效时间2分钟!
redisUtil.hset("captchaCache:", imgUUID, code, 120);
// 将封装好的验证码对象响应给前端
return Result.success(imgResult);
}
UUID工具类如下:
package com.sany.crane.oa.common.util;
import java.util.UUID;
public class UUIDUtil {
/**
* 获得4个长度的十六进制的UUID
*
* @return UUID
*/
public static String get4UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[1];
}
/**
* 获得8个长度的十六进制的UUID
*
* @return UUID
*/
public static String get8UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0];
}
/**
* 获得12个长度的十六进制的UUID
*
* @return UUID
*/
public static String get12UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0] + idd[1];
}
/**
* 获得16个长度的十六进制的UUID
*
* @return UUID
*/
public static String get16UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0] + idd[1] + idd[2];
}
/**
* 获得20个长度的十六进制的UUID
*
* @return UUID
*/
public static String get20UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0] + idd[1] + idd[2] + idd[3];
}
/**
* 获得24个长度的十六进制的UUID
*
* @return UUID
*/
public static String get24UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0] + idd[1] + idd[4];
}
/**
* 获得32个长度的十六进制的UUID
*
* @return UUID
*/
public static String get32UUID() {
UUID id = UUID.randomUUID();
String[] idd = id.toString().split("-");
return idd[0] + idd[1] + idd[2] + idd[3] + idd[4];
}
}