1 验证码
1.1 什么是验证码?
我搜索百度,从百度百科中拿出来一段话:
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。
1.2 验证码的历史
验证码这个词最早是在2002年出现在卡内基梅隆大学。
1.3 验证码的作用
防止恶意破解密码、刷票、论坛灌水、刷页。
1.4 验证码的分类
验证码整体来说分为五类,他们分别是
- 静态图片内容验证码:一般都是输入一些随机码,形式上还可以有计算
- Gif动画验证码:动态展示一些随机码,形式上还可以有计算
- 手机短信验证码:发送短信随机验证码
- 手机语音验证码:语音验证码,也可以让你读出一些随机码或计算结果
- 视频验证码:点击视频中的一些随机码,计算结果
了解了验证码之后,我们开始手工实现一些验证码,当然你也可以使用一些已经封装好的工具来实现这个功能,例如Hutool、Google、Baidu、阿里等大佬们封装好的一些工具类直接实现验证码。
2 实现一个验证码
如果我们想要实现一个验证码,那么就得先要了解下验证码的实现原理以及代码实现。
2.1 验证码的原理
网页之间实现验证码大体上有如下步骤:
1)生成一个随机数
2)将随机数写入图片
3)将图片返回到网页
4)用户获取到图片信息,输入图片内容
5)用户提交内容,服务端验证内容的准确性
整体的流程,使用一张图可能会更形象些,大家可以在这图里面体会下。
这里简单使用“静态图片内容验证码”来作为一个实验例子,来实现我们需要的验证码功能。
2.1 生成一个随机码
生成一个随机码的步骤大体如下:
1)创建一个类RandImagesVerifCode
2)编写一个名叫randomString(String baseString, int length)的方法
3)测试实验
完整的代码如下:
代码语言:javascript复制package com.liuyc.tooljdk.image;
import java.util.concurrent.ThreadLocalRandom;
/**
* <p> Picture Verification Code </p>
*
* @author Aion.Liu
* @version v1.0.0
* @description TODO
* @since 2022/9/1 22:24
*/
public class RandImagesVerifCode {
public static final String BASE_NUMBER = "0123456789";
public static void main(String[] args) {
String resultCode = RandImagesVerifCode.randomString(BASE_NUMBER, 4);
System.out.println(resultCode);
}
/**
* 生成一个随机数
* @param baseString 指定的源中获取随机数
* @param length 获取随机数的个数
* @return
*/
public static String randomString(String baseString, int length) {
if (baseString == null || baseString == "") {
return "";
} else {
StringBuilder sb = new StringBuilder(length);
if (length < 1) {
length = 1;
}
int baseLength = baseString.length();
for (int i = 0; i < length; i) {
int number = ThreadLocalRandom.current().nextInt(baseLength);
sb.append(baseString.charAt(number));
}
return sb.toString();
}
}
// 将验证码写入到redis
public boolean set(String key, Object value, long time) {
try {
if (time > 0L) {
// this.redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
// this.set(key, value);
}
return true;
} catch (Exception var6) {
var6.printStackTrace();
return false;
}
}
}
分步骤解析:
1、public static final String BASE_NUMBER = "0123456789";
这里是指定一个数据源头,也就是从这里面随机多少个数字出来。关于数据源头,这里可以任意指定。例如我们也可以指定字母数字组合
public static final String BASE_CHAR_NUMBER = "abcdefghijklmnopqrstuvwxyz0123456789";
2、public static String randomString(String baseString, int length)
这里是生成随机数的方法,有两个参数。参数的用途已经写在了代码中。长度我们一般使用4~5位,其他少了也不好,多了又太过于麻烦。
3、public boolean set(String key, Object value, long time)
将验证码写入到redis,这是redis的key以及值,当然,还需要设置这个验证码的时效,过期之后这个验证码就不可以再使用,需要我们重新获取。
2.2 将验证码写入一张图片
这个步骤比较简单
1)生成一张图片,将验证码写入图片
2)将图片写入到流中并返回
代码语言:javascript复制 public static void main(String[] args) throws IOException {
String resultCode = RandImagesVerifCode.randomString(BASE_NUMBER, 4);
System.out.println(resultCode);
String base64 = RandImagesVerifCode.produceImage(resultCode);
System.out.println(base64);
}
public static String produceImage(String resultCode) throws IOException {
/**
* 定义图形大小
*/
final int WIDTH = 105;
/**
* 定义图形大小
*/
final int HEIGHT = 35;
/**
* 定义干扰线数量
*/
final int COUNT = 200;
/**
* 干扰线的长度=1.414*lineWidth
*/
final int LINE_WIDTH = 2;
/**
* 生成一张图片,将结果写入到图片
*/
// 在内存中创建图象
final BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
final Graphics2D graphics = (Graphics2D) image.getGraphics();
// 设定背景颜色
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, WIDTH, HEIGHT);
// 设定边框颜色
graphics.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
final Random random = new Random();
// 随机产生干扰线,使图象中的认证码不易被其它程序探测到
for (int i = 0; i < COUNT; i ) {
final Random randomColor = new Random();
final int r = 150 randomColor.nextInt(50);
final int g = 150 randomColor.nextInt(50);
final int b = 150 randomColor.nextInt(50);
graphics.setColor(new Color(r, g, b));
// 保证画在边框之内
final int x = random.nextInt(WIDTH - LINE_WIDTH - 1) 1;
final int y = random.nextInt(HEIGHT - LINE_WIDTH - 1) 1;
final int xl = random.nextInt(LINE_WIDTH);
final int yl = random.nextInt(LINE_WIDTH);
graphics.drawLine(x, y, x xl, y yl);
}
// 取随机产生的认证码
for (int i = 0; i < resultCode.length(); i ) {
// 设置字体颜色
graphics.setColor(Color.BLACK);
// 设置字体样式
graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));
// 设置字符,字符间距,上边距
graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) 8, 26);
}
// 图象生效
graphics.dispose();
/**
* 将图片写图图片流中
*/
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
//写入流中
ImageIO.write(image, "JPEG", byteStream);
//转换成字节
byte[] bytes = byteStream.toByteArray();
//转换成base64串
String base64 = Base64.getEncoder().encodeToString(bytes).trim();
//删除 rn
base64 = base64.replaceAll("n", "").replaceAll("r", "");
return "data:image/jpg;base64," base64;
}
执行后的结果如下:
4408
图片的转码如下:
代码语言:javascript复制data:image/jpg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAjAGkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4 Tl5ufo6erx8vP09fb3 Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3 Pn6/9oADAMBAAIRAxEAPwD3qZJXaHy5TGqvmTAGWXB45B74p8kgiQuwYgf3VLH8hzUVwkzSW7QkYSTMgLEZXaR2HPJBx7V5t8WvH/8AwhGi XYvbp4h1FdixK5byo R55 XkjG1c4ySfvBCKAPS8xfaseZ 2fc3n7ueu3OOvfFEE/nqD5UsZKq2JFwcEZ/PsR7fTPBfCG9m1H4Z6LJdXck97O0000rzbpnxcOAzE8sDtC5 g nbQiKa8F150bnYyw W QU XccdMhh1HtmgCxszc ZmQYTb975Tk nqMdff8lZGaRGEjKFzlBjDfXjP5YrxP4xfEiTwxFHoHhy7hS nhK3E0buZrSM42rzwHOWwSdyg8Bchq9M8FxLdeAvDMs25pBp9tPv3EEuYgSxPcncc565OaANsTmaSWOBovkXG4kkhun3eMjg856gjsakjEu/fIwAKKPLXkK3OSDwT1Hbt71mazfN4f0DVNXW3E32W3muniMuN xWb720nJwo9AOnTB a18b M00n/hYn/CTTbzrRs10clzbFfK3n5C NgBC4xnnO4NzQB9PKGMdwVvgFjd8MCG25Gfmz/dJOBxxjNWJJEV0DCTO4Y2qxGTkc44x9eBx7Vm HrpdZ0HS9baIwy31rFdtGkjbQzxqSMdDgYHI7VifE7XLzQfhvq raf 7uYNiJ5qZHzSrHkjv97cM8HAyCMggHUwm8ZI2lWFSXJdBn5UwcAHu2cZPTrUybN0m1snd8w3ZwcDj24wce e9fKz/EjX9O0XSfEll4yluNYuJZYL/SpsGGNEKeWUhCBEBUDLAgnd8pyHx9S5aC3eR0Qy9W8tT856A4AJ6AeuPfFAExZVKgsAWOACep6/0NLVO2MbXMyD7RuRhI29iVVmX7o5wcDnHIG4fhcoAiMbh1WPakXLNtHO7IP0wfmz35r5N1u/8AE76N4o1DxF4Q1VNQ1nyUm1Sa2aCG2iSSMqgUxdyirkvz8vBIyfrNtpnjHmFXwSEDfeHGePYkc9vx5yte0m18TaFeaXf2sd1au4EluZHUybCGUbgVKkkKc8j6g0AeefAfWL2/8AjR306ayjskb7LfPkrdb5JSxQFQDsOAcE8kZxXoGsXE m GLu6sbQnULK1k ywBWkDyKmVjGOWViFHYnIHDYxPomj2Ph7SbLS9Gtfs mxbisbMzFAxLfxndyzHrnHTHpYhjuQqyyL/pL5WQh2MacHBC7sY4Hoec8cigD5I8R3GtweDJrTWfC2p2d5f6v/aF3q13AYlnlKyAIq UuBh2YDccHcQBkgfQvwi1fUNW8Eacl1pNzp0NlbR20Xnlj9pURptmUsoyrc4A4Hq2Rt6HXfDmleMbIaZ4h0wzwQyLOqiZ1QvhlBDKVJwCcg v0NXtN0mHS9JtdMgBa2gijhUM5 RUQKu3qf4QevUk5oAwfiTp0OofDvxJG8lxGyWElwGjcjBjUsAM5AB27WA6qxHevm3J/wCGfANpwfFRO7sP9F/z VfX0il0KrI0ZP8AEuMj8wRXmdz8GfAdp4mXXZ4ZkjM3m/2eWX7IG6cptyE3EHBbbzjG35aAOl HNutj8N/DULXU0xksIpFa4k3Nl037Af7qgkKOyqB2rK LeqtpXwy1WS60 2vPOVIvLkieSLLSAHdtwVwOVfI YDoSoPaTI8LLNFJtiU5kiwoUgk7mzjIIznrzj3qrc6VYa3o11pWpoL 1mBiuI5hjJ4J915wwI6ZBU9KAPkDU/Dt94MsPCnie11S0nfUU 2W7W/z/AGeWJ1O1sjBK5TPowZSOMn6 i1KSa0tr8215bpPCjx28yKpDOBhJBglHycHnAx17VxNh8FPC9hJZJdXGsatbWpYWtpf3Ikgt2Y7ywQKo5K8g5Vs8g16QsquwC5Ksu5XHKkfX8vz tICK3t/IISLKQoX TAwSxDZGOgHIxVioWYF0kj3yAExkIw2jnknJ7EY9eTxU1MBkkSSqFcZAYN17ggj9QKfRRQAUUUUAIyqwwwBGQeR3HIpaKKACojbQM5cwxlyQxYqMkjofqKKKAHSQxSkGSJHIBA3KDgHg/nT6KKACkVVQYVQoyTgDHJ5NFFAAFVSxVQCxyxA6nGOfwApaKKAP/9k=
我们可以使用任意编写一个Html,然后将返回的数据写入img标签的src中,
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAjAGkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4 Tl5ufo6erx8vP09fb3 Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3 Pn6/9oADAMBAAIRAxEAPwD3qZJXaHy5TGqvmTAGWXB45B74p8kgiQuwYgf3VLH8hzUVwkzSW7QkYSTMgLEZXaR2HPJBx7V5t8WvH/8AwhGi XYvbp4h1FdixK5byo R55 XkjG1c4ySfvBCKAPS8xfaseZ 2fc3n7ueu3OOvfFEE/nqD5UsZKq2JFwcEZ/PsR7fTPBfCG9m1H4Z6LJdXck97O0000rzbpnxcOAzE8sDtC5 g nbQiKa8F150bnYyw W QU XccdMhh1HtmgCxszc ZmQYTb975Tk nqMdff8lZGaRGEjKFzlBjDfXjP5YrxP4xfEiTwxFHoHhy7hS nhK3E0buZrSM42rzwHOWwSdyg8Bchq9M8FxLdeAvDMs25pBp9tPv3EEuYgSxPcncc565OaANsTmaSWOBovkXG4kkhun3eMjg856gjsakjEu/fIwAKKPLXkK3OSDwT1Hbt71mazfN4f0DVNXW3E32W3muniMuN xWb720nJwo9AOnTB a18b M00n/hYn/CTTbzrRs10clzbFfK3n5C NgBC4xnnO4NzQB9PKGMdwVvgFjd8MCG25Gfmz/dJOBxxjNWJJEV0DCTO4Y2qxGTkc44x9eBx7Vm HrpdZ0HS9baIwy31rFdtGkjbQzxqSMdDgYHI7VifE7XLzQfhvq raf 7uYNiJ5qZHzSrHkjv97cM8HAyCMggHUwm8ZI2lWFSXJdBn5UwcAHu2cZPTrUybN0m1snd8w3ZwcDj24wce e9fKz/EjX9O0XSfEll4yluNYuJZYL/SpsGGNEKeWUhCBEBUDLAgnd8pyHx9S5aC3eR0Qy9W8tT856A4AJ6AeuPfFAExZVKgsAWOACep6/0NLVO2MbXMyD7RuRhI29iVVmX7o5wcDnHIG4fhcoAiMbh1WPakXLNtHO7IP0wfmz35r5N1u/8AE76N4o1DxF4Q1VNQ1nyUm1Sa2aCG2iSSMqgUxdyirkvz8vBIyfrNtpnjHmFXwSEDfeHGePYkc9vx5yte0m18TaFeaXf2sd1au4EluZHUybCGUbgVKkkKc8j6g0AeefAfWL2/8AjR306ayjskb7LfPkrdb5JSxQFQDsOAcE8kZxXoGsXE m GLu6sbQnULK1k ywBWkDyKmVjGOWViFHYnIHDYxPomj2Ph7SbLS9Gtfs mxbisbMzFAxLfxndyzHrnHTHpYhjuQqyyL/pL5WQh2MacHBC7sY4Hoec8cigD5I8R3GtweDJrTWfC2p2d5f6v/aF3q13AYlnlKyAIq UuBh2YDccHcQBkgfQvwi1fUNW8Eacl1pNzp0NlbR20Xnlj9pURptmUsoyrc4A4Hq2Rt6HXfDmleMbIaZ4h0wzwQyLOqiZ1QvhlBDKVJwCcg v0NXtN0mHS9JtdMgBa2gijhUM5 RUQKu3qf4QevUk5oAwfiTp0OofDvxJG8lxGyWElwGjcjBjUsAM5AB27WA6qxHevm3J/wCGfANpwfFRO7sP9F/z VfX0il0KrI0ZP8AEuMj8wRXmdz8GfAdp4mXXZ4ZkjM3m/2eWX7IG6cptyE3EHBbbzjG35aAOl HNutj8N/DULXU0xksIpFa4k3Nl037Af7qgkKOyqB2rK LeqtpXwy1WS60 2vPOVIvLkieSLLSAHdtwVwOVfI YDoSoPaTI8LLNFJtiU5kiwoUgk7mzjIIznrzj3qrc6VYa3o11pWpoL 1mBiuI5hjJ4J915wwI6ZBU9KAPkDU/Dt94MsPCnie11S0nfUU 2W7W/z/AGeWJ1O1sjBK5TPowZSOMn6 i1KSa0tr8215bpPCjx28yKpDOBhJBglHycHnAx17VxNh8FPC9hJZJdXGsatbWpYWtpf3Ikgt2Y7ywQKo5K8g5Vs8g16QsquwC5Ksu5XHKkfX8vz tICK3t/IISLKQoX TAwSxDZGOgHIxVioWYF0kj3yAExkIw2jnknJ7EY9eTxU1MBkkSSqFcZAYN17ggj9QKfRRQAUUUUAIyqwwwBGQeR3HIpaKKACojbQM5cwxlyQxYqMkjofqKKKAHSQxSkGSJHIBA3KDgHg/nT6KKACkVVQYVQoyTgDHJ5NFFAAFVSxVQCxyxA6nGOfwApaKKAP/9k=
" />
</body>
</html>
打开网页后调试下结果如下结果。
现在,我们便完成了网页验证码的开发以及回显示到网页中啦。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!