基本原理就是用Java画张指定大小的图片,在图片上随机摆上若干个数字或字母,数字或字母要有一定的倾斜和位移,要变一下字体和颜色,再画几条干扰线,然后就可以返回给客户端了。
创建文件 CaptchaUtil.java
,代码如下:
// Write your package
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.Random;
public class CaptchaUtil {
private static Random random = new Random();
private int width = 165;
private int height = 45;
private int lineSize = 30;
private int randomStrNum = 6;
private String randomString = "0123456789abcdefghijklmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWSYZ";
private final String sessionKey = "CAPTCHA_CODE";
private static Color getRandomColor(int fc, int bc) {
fc = Math.min(fc, 255);
bc = Math.min(bc, 255);
int r = fc + random.nextInt(bc - fc - 16);
int g = fc + random.nextInt(bc - fc - 14);
int b = fc + random.nextInt(bc - fc - 12);
return new Color(r, g, b);
}
private Font getFont() {
return new Font("Times New Roman", Font.ROMAN_BASELINE, 40);
}
private void drawLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(20);
int yl = random.nextInt(10);
g.drawLine(x, y, x + xl, y + yl);
}
private String getRandomString(int num) {
num = num > 0 ? num : randomString.length();
return String.valueOf(randomString.charAt(random.nextInt(num)));
}
private String drawString(Graphics g, String randomStr, int i) {
g.setFont(getFont());
g.setColor(getRandomColor(108, 190));
// System.out.println(random.nextInt(randomString.length()));
String rand = getRandomString(random.nextInt(randomString.length()));
randomStr += rand;
g.translate(random.nextInt(3), random.nextInt(6));
g.drawString(rand, 40 * i + 10, 25);
return randomStr;
}
public boolean validateCode(HttpServletRequest request, String captcha) {
HttpSession session = request.getSession();
if (session == null || captcha == null) {
return false;
}
if (session.getAttribute(sessionKey) == null) {
return false;
}
return session.getAttribute(sessionKey).toString().toLowerCase().equals(captcha.toLowerCase());
}
public void clear(HttpServletRequest request) {
HttpSession session = request.getSession();
session.removeAttribute(sessionKey);
}
public void getRandomCodeImage(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.fillRect(0, 0, width, height);
g.setColor(getRandomColor(105, 189));
g.setFont(getFont());
// 干扰线
for (int i = 0; i < lineSize; i++) {
drawLine(g);
}
// 随机字符
String randomStr = "";
for (int i = 0; i < randomStrNum; i++) {
randomStr = drawString(g, randomStr, i);
}
System.out.println("随机字符:" + randomStr);
g.dispose();
// 移除之前的session中的验证码信息
session.removeAttribute(sessionKey);
// 重新将验证码放入session
session.setAttribute(sessionKey, randomStr);
try {
// 将图片以png格式返回,返回的是图片
ImageIO.write(image, "PNG", response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
// 生成随机图片的base64编码字符串
public String getRandomCodeBase64(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.fillRect(0, 0, width, height);
g.setColor(getRandomColor(105, 189));
g.setFont(getFont());
// 干扰线
for (int i = 0; i < lineSize; i++) {
drawLine(g);
}
// 随机字符
String randomStr = "";
for (int i = 0; i < randomStrNum; i++) {
randomStr = drawString(g, randomStr, i);
}
System.out.println("随机字符:" + randomStr);
g.dispose();
session.removeAttribute(sessionKey);
session.setAttribute(sessionKey, randomStr);
String base64String = "";
try {
// 直接返回图片
// ImageIO.write(image, "PNG", response.getOutputStream());
// 返回 base64
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);
byte[] bytes = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
base64String = encoder.encodeToString(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return base64String;
}
}
可随机生成图片或Base64。在字母中去掉了o
和I
这两个容易引起误判的字母。
接下来就是 Controller,创建图片的 Controller:
@GetMapping(value = "/api/captcha")
@ApiOperation("获取验证码")
public void getCaptchaImg(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
try {
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expire", "0");
response.setHeader("Pragma", "no-cache");
ValidateCodeUtil validateCode = new ValidateCodeUtil();
validateCode.getRandomCodeImage(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
在登录的 Controller 中进行验证:
ValidateCodeUtil validateCode = new ValidateCodeUtil();
if (!validateCode.validateCode(request, user.getCaptcha())) {
throw new Exception("验证码错误");
} else {
validateCode.clear(request);
}
验证成功后,clear
一下。