验证码
...大约 5 分钟
验证码
简介
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试 )的缩写,是一种区分用户是计算机还是人的公共全自动程序。2002年,路易斯和他的小伙伴在卡内基梅隆第一次提出了CAPTCHA(验证码)这样一个程序概念。该程序是指,向请求的发起方提出问题,能正确回答的即是人类,反之则为机器。这个程序基于这样一个重要假设:提出的问题要容易被人类解答,并且让机器无法解答。可以有效的防止某些特定程序以暴力方式不断进行登录尝试。验证码的不断发展其实是随着其破解功能的逐步强大而跟着演进的,这是一个攻防博弈的精彩世界。
手写图形验证码
如果设计的图形验证码干扰项不是很多,则非常不安全,一些机器学习技术可以轻松实现识别,并且准确率是100%。具体见"我的笔记"中的《Tess4j光学识别》这篇文章
package com.ctg.MyAllClass.LittleTools;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
/**
* 1. @ClassDescription:自动生成一张验证码图片
* 2. @author: TenSoFlow
* 3. @date: 2023年10月11日 14:25
*/
@SuppressWarnings("all")
public class Captcha
{
// 验证码图片宽度
private static int width = 150;
// 验证码图片高度
private static int height = 50;
// 图片后缀
private static String type = "jpg";
// 图片名称
private static String name = "test";
// 设置图片的生成位置
private static String position = "D:\\Desktop\\";
// 验证码能包含的符号 没有0与o 和l与1 因为他们很相似
private static String str = "abcdefghigkmnpqrstuvwxyz23456789";
// 图片类型
private static int imageType = BufferedImage.TYPE_INT_RGB;
// 生成文件对象
private static File file = new File( position + name + "." + type);
public static void main(String[] args) throws IOException
{
// 1.准备画板或者叫一张白纸
BufferedImage image = new BufferedImage(width,height,imageType);
// 2.准备画笔
Graphics graphics = image.getGraphics();
// 给笔设置颜色
graphics.setColor(Color.YELLOW);
// 给笔设置字体
graphics.setFont(new Font("楷体",Font.BOLD,25));
// 用笔画四个字符串(圆 矩形都可以画) 参数基本都是内容加位置
int x = 35;
int y = 35;
Random random = new Random();
for(int i=0;i<4;i++)
{
// 随机取一个字符
String temp = String.valueOf(str.charAt(random.nextInt(str.length())));
// 把字符画上去
graphics.drawString(temp,x,y);
x += 30;
}
// 3.画干扰线
for(int i=0;i<20;i++)
{
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
graphics.drawLine(x1,y1,x2,y2);
}
// 4.绘制完成 生成图片 存在本地磁盘
ImageIO.write(image,type,file);
}
}
项目中使用手写图形验证码
SpringBoot项目中前端发送请求,后端响应一张验证码图片的实现。
前端
<img id="captcha" src="http://localhost:8080/login/verifyCode"onclick="ChangeVerifyCode(this)">
<script>
var baseURL = 'http://localhost:8080'
// 更新验证码函数
function ChangeVerifyCode(obj)
{
// 得到当前时间
var timeNow = new Date().getTime();
obj.src = baseURL + "/login/verifyCode?d=" + timeNow;
}
</script>
后端
GenerateImage生成图像工具类
package com.utils;
import lombok.Data;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;
/**
* 1. @ClassDescription:产生验证码图片
* 2. @author: TenSoFlow
* 3. @date: 2023年10月11日 17:03
*/
@Data
public class GenerateImage
{
// 验证码真实字符串
private String str = "";
// 验证码图片
private ByteArrayInputStream image;
// 验证码图片宽度
private static int width = 150;
// 验证码图片高度
private static int height = 50;
// 构造方法创建对象时就使用init方法创建好图片存到ByteArrayInputStream image中
private GenerateImage()
{
init();
}
// 得到该工具类对象
public static GenerateImage getInstance()
{
return new GenerateImage();
}
// 图片后缀
private static String type = "jpg";
// 验证码能包含的符号 没有0与o 和l与1 因为他们很相似
private static String strs = "abcdefghigkmnpqrstuvwxyz23456789";
// 图片类型
private static int imageType = BufferedImage.TYPE_INT_RGB;
private void init()
{
// 1.准备画板或者叫一张白纸
BufferedImage image = new BufferedImage(width,height,imageType);
// 2.准备画笔
Graphics graphics = image.getGraphics();
// 给笔设置颜色
graphics.setColor(Color.YELLOW);
// 给笔设置字体
graphics.setFont(new Font("楷体",Font.BOLD,25));
// 用笔画四个字符串(圆 矩形都可以画) 参数基本都是内容加位置
int x = 35;
int y = 35;
Random random = new Random();
for(int i=0;i<4;i++)
{
// 随机取一个字符
String temp = String.valueOf(strs.charAt(random.nextInt(strs.length())));
str += temp;
// 把字符画上去
graphics.drawString(temp,x,y);
x += 30;
}
// 3.画干扰线
for(int i=0;i<20;i++)
{
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
graphics.drawLine(x1,y1,x2,y2);
}
// 4.绘制完成 生成图片
graphics.dispose();
ByteArrayInputStream inputStream = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ImageOutputStream imageOut = ImageIO.createImageOutputStream(outputStream);
ImageIO.write(image,type,imageOut);
imageOut.close();
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
}catch (Exception e)
{
System.out.println("验证码图片生成失败");
}
this.image = inputStream;
}
}
LoginController类
package com.tensoflow.controller;
import com.utils.GenerateImage;
import com.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* 1. @ClassDescription:生成验证码的Controller类
* 2. @author: TenSoFlow
* 3. @date: 2023年10月11日 17:13
*/
@CrossOrigin(origins = "http://localhost:63342" , allowCredentials = "true")
@RestController
@RequestMapping("/login")
@Slf4j
public class LoginController
{
@GetMapping("/verifyCode")
public void generateImage(HttpServletRequest request, HttpServletResponse response)
{
// 打印日志
log.info("更新验证码");
// 得到工具类
GenerateImage imageUtil = GenerateImage.getInstance();
// 得到输入流
ByteArrayInputStream image = imageUtil.getImage();
// 得到真实验证码文字并存入session中
String verifyCode = imageUtil.getStr();
request.getSession().setAttribute("verifyCode",verifyCode);
// 设置返回类型为图像
response.setContentType("image/jpg");
// 使用response返回图像
byte[] bytes = new byte[1024];
try(ServletOutputStream out = response.getOutputStream())
{
while(image.read(bytes) != -1)
{
out.write(bytes);
}
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
Powered by Waline v2.15.8