效果
实现思路
验证码图片的url由后端的一个Controller生成,前端请求这个Controller接口的时候根据当前时间生成一个uuid,并把这个uuid在前端使用localStorage缓存起来,下一次还是从缓存中获取。
Controller生成验证码之后,把前端传过来的uuid通过redis缓存起来,这里分两次缓存
- 缓存uuid
- 以uuid为key,缓存验证码
这样,当点击登录按钮将数据提交到后台登录接口时,会从redis中获取uuid,然后通过这个uuid去获取验证码,和前端用户输入的验证码进行比较。
简单验证码
生成随机字符(如数字、字母)的字符串,并将这些字符绘制到一张图片上,最后输出这张图片。
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class CaptchaGenerator {
private static final int WIDTH = 100;
private static final int HEIGHT = 40;
private static final Random RANDOM = new Random();
public static void main(String[] args) {
// 生成验证码文本
String captchaText = generateCaptchaText(6);
// 生成验证码图片
File outputFile = new File("captcha.png");
generateCaptchaImage(captchaText, outputFile);
System.out.println("Captcha generated: " + captchaText);
}
private static String generateCaptchaText(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
// 随机选择字符类型
int type = RANDOM.nextInt(3);
if (type == 0) { // 数字
sb.append(RANDOM.nextInt(10));
} else if (type == 1) { // 小写字母
sb.append((char) ('a' + RANDOM.nextInt(26)));
} else { // 大写字母
sb.append((char) ('A' + RANDOM.nextInt(26)));
}
}
return sb.toString();
}
private static void generateCaptchaImage(String captchaText, File outputFile) {
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
// 设置背景色
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// 设置字体
Font font = new Font("Arial", Font.BOLD, 24);
g2d.setFont(font);
// 绘制验证码文本
g2d.setColor(Color.BLACK);
FontMetrics fontMetrics = g2d.getFontMetrics();
int x = (WIDTH - fontMetrics.stringWidth(captchaText)) / 2;
int y = ((HEIGHT - fontMetrics.getHeight()) / 2) + fontMetrics.getAscent();
g2d.drawString(captchaText, x, y);
// 释放资源
g2d.dispose();
// 输出图片
try {
ImageIO.write(bufferedImage, "png", outputFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
工具包
- Hutool:是一个多功能的工具包,其中有验证码的功能hutool-captcha
- easy-captcha:专注验证码
Hutool demo
1、添加依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
2、写一个控制器
package com.zhangyu.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.json.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
public class CaptchaController {
@GetMapping("/captcha")
public Map captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 生成验证码图片
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 4);
// 获取验证码图片上的文字
String captchaText = lineCaptcha.getCode();
// 将验证码文字放入session,键名为 CAPTCHA_KEY
request.getSession().setAttribute("CAPTCHA_KEY", captchaText);
System.out.println("文字是" + captchaText);
// 输出图片
BufferedImage image = lineCaptcha.getImage();
// 图片转base64
String base = ImageToBase64.convertImageToBase64(image);
System.out.println("base文字是" + base);
// 组装数据
Map resultMap = new HashMap();
resultMap.put("code", captchaText);
resultMap.put("img", "data:image/png;base64," + base);
return resultMap;
}
@PostMapping("/submit")
public String submit(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
String captcha = jsonObject.getStr("captcha");
String sessionCaptcha = (String) request.getSession().getAttribute("CAPTCHA_KEY");
if (captcha.equalsIgnoreCase(sessionCaptcha)) {
return "验证码正确!";
} else {
return "验证码错误,请重试!";
}
}
}
package com.zhangyu.controller;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.io.IOException;
public class ImageToBase64 {
public static String convertImageToBase64(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos); // 可以改为需要的图片格式,如"jpg"
byte[] imageBytes = baos.toByteArray();
return Base64.getEncoder().encodeToString(imageBytes);
}
}
3、前端测试
这里我用vue写了一个简单的demo
<template>
<div class="home">
<img :src="imgUrl" @click="getCaptchaImg()" alt="captcha" title="点击刷新">
<input type="text" name="captcha" placeholder="输入验证码" v-model="text">
<button type="submit" @click="submitCaptcha()">提交</button>
</div>
</template>
<script>
import baseApi from '@/api/base'
export default {
name: 'HomeView',
data () {
return {
imgUrl: '',
text: ''
}
},
methods: {
async getCaptchaImg() {
const res = await baseApi.getCaptchaImg()
this.imgUrl = res.img
},
async submitCaptcha() {
const params = {
captcha: this.text,
}
const res = await baseApi.submitCaptcha(params)
}
},
async mounted () {
this.getCaptchaImg()
}
}
</script>
import ajax from '@/libs/ajax'
const baseApi = {
getCaptchaImg: () => ajax.request({
url: '/captcha',
method: 'get'
}),
submitCaptcha: (data) => ajax.request({
url: '/submit',
method: 'post',
data
})
}
export default baseApi
devServer: {
port: 9001,
open: false,
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: 'http://192.168.18.21:9090/'
},
4、效果
总结
这里只是用Hutool工具包做了一个demo,easy-captcha我就不展示了也差不多。
真实的场景是返回给前端base64图片和一个uuid,并不是我这里展示的图片文字,然后提交的时候前端把uuid和输入内容传递给后端,由于我这里只是demo所以没有牵扯uuid