一、验证码概述
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序
验证码的作用:
- 防止刷票、论坛灌水、刷页
- 防止黑客恶意破解密码,盗取用户数据
验证码通常使用一些线条和一些不规则的字符组成,主要作用是为了防止一些黑客把密码数据化盗取。有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登录尝试,实际上使用验证码是现在很多网站通行的方式(比如招商银行的网上个人银行,百度社区),我们利用比较简易的方式实现了这个功能。虽然登录麻烦一点,但是对网友的密码安全来说这个功能还是很有必要,也很重要
- 防止恶意注册、登录
验证码一般是防止批量注册的,人眼看起来都费劲,何况是机器。几乎所有正规的论坛都要求注册时输入验证码,这是为了防止乱发垃圾广告的人用注册机来恶意注册
二、Java原生验证码
创建好需要保存的Bean类,保存好结果和过期时间等,最后直接把这个类放在session中
public class CheckCode {
// 验证码字符
private String code;
// 过期时间
private LocalDateTime expireTime;
/**
* @param code 验证码字符
* @param expireTime 过期时间,单位秒
*/
public CheckCode(String code, int expireTime) {
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireTime);
}
public CheckCode(String code) {
// 默认验证码 60 秒后过期
this(code, 60);
}
// 是否过期
public boolean isExpired() {
return this.expireTime.isBefore(LocalDateTime.now());
}
public String getCode() {
return this.code;
}
}
1、随机数字验证码
// 验证码图片边框宽度
int WIDTH = 120;
// 验证码图片边框高度
int HEIGHT = 45;
// 验证码有效时间 60s
int expireIn = 60;
// 普通验证码
int length = 4; // 验证码位数
/**
* 普通验证码
*/
public void createNumberImageCode(HttpServletRequest request,
HttpServletResponse response) {
// 设置响应报头信息
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 设置响应的MIME类型
response.setContentType("image/jpeg");
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(185, 250));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setFont(new Font("Times New Roman", Font.ITALIC, 35));
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(WIDTH);
int y = random.nextInt(HEIGHT);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
StringBuilder sRand = new StringBuilder();
for (int i = 0; i < length; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand.append(rand);
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 25 * i + 12, 32);
}
g.dispose();
// 放入session缓存,默认60s过期
CheckCode checkCode = new CheckCode(sRand.toString(),expireIn);
HttpSession se = request.getSession();
se.setAttribute(Constants.KAPTCHA_SESSION_KEY, checkCode);
try {
OutputStream os = response.getOutputStream();
// 输出图像到页面
ImageIO.write(image, "JPEG", os);
} catch (IOException e) {
e.printStackTrace();
}
}
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
2、随机数字和字母验证码
// 验证码图片边框宽度
int WIDTH = 120;
// 验证码图片边框高度
int HEIGHT = 45;
// 验证码有效时间 60s
int expireIn = 60;
// 普通验证码
int length = 4; // 验证码位数
public void createNumberAndLetterImageCode(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 设置响应报头信息
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 设置响应的MIME类型
response.setContentType("image/jpeg");
//画板
BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
//画笔
Graphics g = image.getGraphics();
//字体
Font font = new Font("微软雅黑", Font.BOLD,35);
//设置字体
g.setFont(font);
//引入背景图片
g.fillRect(0, 0, WIDTH, HEIGHT);
//随机数
Random random = new Random();
//要随机的字符串
String template = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder s = new StringBuilder();
char tempNum;
for (int i = 0; i < length; i++){
//获取随机出的字符
int tempIndex = random.nextInt(template.length()-1);
tempNum = template.charAt(tempIndex);
//拼成字符串
s.append(tempNum);
//设置颜色
Color color = new Color(20+random.nextInt(110),20+random.nextInt(110),random.nextInt(110));
g.setColor(color);
//字母写入图片
g.drawString(String.valueOf(tempNum),25 * i + 12, 32);
}
// 放入session缓存,默认60s过期
CheckCode checkCode = new CheckCode(sRand.toString(),expireIn);
HttpSession se = request.getSession();
se.setAttribute(Constants.KAPTCHA_SESSION_KEY, checkCode);
//获取流发送给前台
ServletOutputStream ots = response.getOutputStream();
ImageIO.write(image,"JPEG",ots);
}
3、运算验证码
// 公共部分
// 验证码图片边框宽度
int WIDTH = 120;
// 验证码图片边框高度
int HEIGHT = 45;
// 验证码有效时间 60s
int expireIn = 60;
// 运算验证码
// 验证码字体高度
int FONT_HEIGHT = HEIGHT - 12;
// 验证码干扰线条数
int INTERFERENCE_LINE = 4;
/**
* 生成运算验证码
*/
public void createSpecialImageCode(HttpServletRequest request,
HttpServletResponse response){
// 设置响应报头信息
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 设置响应的MIME类型
response.setContentType("image/jpeg");
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Font mFont = new Font("Arial", Font.PLAIN, 18);
Graphics g = image.getGraphics();
Random rd = new Random();
// 设置背景颜色
g.setColor(new Color(rd.nextInt(55) + 200, rd.nextInt(55) + 200, rd
.nextInt(55) + 200));
g.fillRect(0, 0, WIDTH, HEIGHT);
// 设置字体
g.setFont(mFont);
// 画边框
g.setColor(Color.black);
g.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
// 验证码字符串
String text = getText();
// 运算表达式
String operationExpression = text.substring(0, text.lastIndexOf("@") - 1);
// 计算结果
String result = text.substring(text.lastIndexOf("@") + 1);
// 放入session缓存,默认60s过期
CheckCode checkCode = new CheckCode(result,expireIn);
HttpSession se = request.getSession();
se.setAttribute(Constants.KAPTCHA_SESSION_KEY, checkCode);
g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd
.nextInt(200)));
// 根据画笔颜色绘制字符
g.drawString(operationExpression, 5, FONT_HEIGHT);
int r = 0;
int gg = 0;
int b = 0;
// 绘制干扰线
int x1, y1, x2, y2;
for (int i = 0; i < INTERFERENCE_LINE; i++) {
// 随机生成rgb颜色值,并设置画笔颜色
r = rd.nextInt(255);
gg = rd.nextInt(255);
b = rd.nextInt(255);
g.setColor(new Color(r, gg, b));
x1 = rd.nextInt(WIDTH);
y1 = rd.nextInt(HEIGHT);
x2 = rd.nextInt(WIDTH);
y2 = rd.nextInt(HEIGHT);
// 绘制线条
g.drawLine(x1, y1, x2, y2);
}
// 释放图形资源
g.dispose();
try {
OutputStream os = response.getOutputStream();
// 输出图像到页面
ImageIO.write(image, "JPEG", os);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取运算验证码
*/
private String getText() {
Random random = new Random(System.currentTimeMillis());
StringBuilder result = new StringBuilder(); // 运算验证码结果
int x = random.nextInt(51);
int y = random.nextInt(51);
int operationalRules = random.nextInt(4);
switch (operationalRules) {
case 0:
result = add(x, y);
break;
case 1:
result = subtract(x, y);
break;
case 2:
result = multiply(x, y);
break;
case 3:
result = divide(x, y);
break;
default:
result = add(x, y);
break;
}
return result.toString();
}
/**
* 加法运算
*/
private StringBuilder add(int x, int y) {
StringBuilder result = new StringBuilder(); // 运算验证码结果
result.append(x);
result.append(" + ");
result.append(y);
result.append(" = ?@");
result.append(x + y);
return result;
}
/**
* 减法运算
*/
private StringBuilder subtract(int x, int y) {
StringBuilder result = new StringBuilder(); // 运算验证码结果
int max = Math.max(x, y);
int min = Math.min(x, y);
result.append(max);
result.append(" - ");
result.append(min);
result.append(" = ?@");
result.append(max - min);
return result;
}
/**
* 乘法运算
*/
private StringBuilder multiply(int x, int y) {
StringBuilder result = new StringBuilder(); // 运算验证码结果
int value = x * y;
result.append(x);
result.append(value > 100 ? " + " : " * ");
result.append(y);
result.append(" = ?@");
result.append(value > 100 ? x + y : x * y);
return result;
}
/**
* 除法运算
*/
private StringBuilder divide(int x, int y) {
StringBuilder result = new StringBuilder(); // 运算验证码结果
int max = Math.max(x, y);
int min = Math.min(x, y);
if (min == 0) {
multiply(max, min);
} else if (max % min == 0) {
result.append(max);
result.append(" / ");
result.append(min);
result.append(" = ?@");
result.append(max / min);
} else {
result.append(max);
result.append(" % ");
result.append(min);
result.append(" = ?@");
result.append(max % min);
}
return result;
}
三、引入三方验证码
三方验证码有很多,这里我就只举例一个
引入依赖
<!--验证码-->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
编写代码
@GetMapping("/")
public void CodeCheck(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设置请求头输入为图片类型
response.setContentType("image/jpg");
// 设置不用缓存,防止验证码不刷新
response.setHeader("Pargam","No-cache");
response.setHeader("Cache-Control","no-cache");
// 设置过期时间,永不失效
response.setDateHeader("Expires",0);
// 算术类型(长,宽,几个数的运算)
ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
captcha.setLen(3); // 几位数运算,默认是两位
System.out.println(captcha.getArithmeticString()); // 获取运算公式 5x0+5=?
System.out.println(captcha.text()); // 获取验证码结果
// 图片英语字母数字类型
//SpecCaptcha captcha = new SpecCaptcha(130, 48);
// 英语字母数字gif类型的
//GifCaptcha captcha = new GifCaptcha(130, 48,4);
// 中文类型的
//ChineseCaptcha captcha = new ChineseCaptcha(130, 48,3);
// 中文gif类型
//ChineseGifCaptcha captcha = new ChineseGifCaptcha(130, 48,4);
// 输出验证码
try {
captcha.out(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}