# 网站实现验证码功能
# 一、验证码
一般来说,网站在登录的时候会生成一个验证码来验证是否是人类还是爬虫,还有一个好处是防止恶意人士对密码进行爆破。
# 二、流程图
# 三、详细说明
# 3.1 后端生成验证码
@Override
public Result<Map<String, String>> getVerificationCode() {
// HuTool 定义图形验证码的长和宽, 验证码的位数,干扰线的条数
// 线段干扰的验证码
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(116, 36, 4, 50);
String uuid = UUID.randomUUID().toString();
String verificationCodeKey = "VerificationCode:" + uuid;
// 存到 redis 中
ValueOperations<String, String> ops = redisTemplate.opsForValue();
ops.set(verificationCodeKey, lineCaptcha.getCode());
// 设置 15 分钟的过期时间
redisTemplate.expire(verificationCodeKey, 15, TimeUnit.MINUTES);
Map<String, String> map = new HashMap<>(2);
map.put("verificationCodeBase64", lineCaptcha.getImageBase64());
map.put("uuid", uuid);
return Result.success(map);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
记得设置验证码过期时间,否则每一次生成验证码都会在 redis
里面产生数据,造成内存浪费。
返回给前端的 json
:
{
"code": 200,
"msg": "操作成功",
"data": {
"uuid": "bc814884-fffb-4943-834a-40cc92decfa6",
"verificationCodeBase64": "iVBORw0KGgoAAAANSUhEUgAAAHQAAAAkCAYAAABYFB7QAAAFtUlEQVR4Xu3YfUxVdRjAcW0zM6atSX84X1JhleMPI6VmtcxexFZs5qq5/mhNpxKV06TMiQrpVssxopClZr7QdFCycutFpmn4QhgkA+J2GaK8eUWUCxeIe70vT/f33J2zc557zr33nPM7B1C+2zPvPc9BNz4751zvGBjttmoMPTCavrzxc3CGOgR9p/klepxbr/W+Rw8N2wq3ZeMYaahhxSuUoeqFdTRPxFHLCtS3en8Xh5ZxLlM2t3Nht9w3FteFTawJsEq4DHXem/eEjZkdyX8+puEN7c5ZD+kHNokTa2/f/7Fs9BQGyisprhRYuFrnl/yGQ4HNRlZr3MI8cXgCU1w16E83bpe9Z+kBNg2URmGFBNjhlBSXjVlRXIaqBKsly0CjNRxhhXgCv3rykjhqGUFF0H/yxmsaXvmvJOOMtHjhFqUVgO3uVTEhx5pmUNuuB+jfoTsK+tOGkzh3Qr0lFxCTjTSjuDHfcrvr9iGoq+kYXXFPgB2OuCsnpNFDmnOVVsO/cemKoEaLCXSwqxYavpwEV09k0JXp8cId15uKYzRDoD4/XM8qBdv41SKm5aABvxcuFc0H++7p4Pf00rUpJazbKhshs2CffPlF2ftI6QXtL6uH5uRsEfHygh1DA3qjKhdvtc76g3RlOPoMVYsCU2itBQIAcZVrIX73fpiS8wvM3HgWZqz7GR5atQvs1wbo6bCsY6z4moK2LUzEiZZ4RU5YA52ZxRAYvGU9qO+/ruCHoHhoOjg3+Fvw0bXhYgVVSw9wv9sHKw80wNTMM4ozbcNpSF7+Pv2xqAmwasDsNtu29Ctw17bh+7FnvhVB2WteRQR1nM7Eq7PHdhjfZ8EzOGqlTdf2izAKqqe1R+wIN2dLBewt74AOpxvcXj/YrvZDepENdwmbzkHK0tc13YppFNZjd0i2oaRXKEOloydVUJ/bCbaCyWDfMyN4cXpkOwE2Em4sRQPtnfUBPWSoi60uBEsMgjFAGrsVC1fv5tImuuaepbfcrr924tV5vSLytxZGcKOBshgqL9icY82I9dmvV+hKrK69D895fMcFuuKedaDB52Xj3tnQkB8H3oFOulVNK6waqMN/Fz3EBTU17yJi1bT20RVsHrst5uGVZaD9rafx6rzywxLxWNe0BZIz+KQGylJCNdrc7D8R1DWo/wNeR9kUHB5ZBuo4tR5Bb9YUyo7zRo0EymKoPGEf/Ogsgvr8AbqSlfrwCtkoJcAaAbYMtPGbBAS95Qp9xJbGUHnBUtD8Nd9JtqHW5D0njtEeyTqPoH1ubVcoBVZC1gNsCaj7Rj1iNh16jK5k8UAVQNsLs3BYDFUJliXF1QO8aGc1gta2hz9Dha67PHhO8ieV+P7dtqdxtBYLrCWg3bV7EbSjjO8/pBS9QgVYYdRghbQCbyhuRKz8E610JfZ9VSees/qQTXZcgNWDq5YloOwLeAbaXfM1XXGPgtIocLSi4Z5v6kGspK0V0O500zUMeHyw8PMqPOd4/U265p4loJeLn0XQ/tZTdMW9aKA0Lbhqrdgf+uJg3vZKOFrdCc4BL37qLW90wgu5f+NuWWEt/TFTsgTUvmcmgnq67XTFPSXQpJRZsuGda9CLYAxOadhzlj1Hrch00Lh9BVDzxb0I6ve4pCtTUgKlUWAtyMltcYrzaMtEOFx5DWHZd7pTPyzHW23u8Rb88t6qTAcVYrBszM0fAm1JoYuwls/OxuEZRU44VYLDXlvdkUk/ysZIiqBCAqwpuL4bIdDWRXSjmhpsYfUJ2RhJgFWqc/F9OGZnBDciqDRusEFICLgh0LMnBOoI/096tNRghSiwUWRaJNi6pFdwhqqYQfkUvM22PCU+O9kE+o7Sk0ZMAiybjD+yxWEJsFYDWwvqdYC/PS2I+kTwzyXBq3QfPWPEJr1qpbgU2EiTlyXgRMpa0DukLeWJsuFdJNhR0BGcEuwo6G3W/2buccxVrdRAAAAAAElFTkSuQmCC"
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Base64
是一种用于将二进制数据编码为 ASCII
字符的编码方法,我们这边用这个编码传递验证码图片,数据在 verificationCodeBase64
。
# 3.2 前端生成验证码图片
# 3.2.1 获取验证码 Base64 并且拼接
const updateVerificationCodeImage = async () => {
// 图像类型
const imageType = 'image/png';
const res = await userGetVerificationCodeApi();
if (res.data) {
if (res.data.data) {
verificationCodeBase64.value = res.data.data.verificationCodeBase64;
uuid.value = res.data.data.uuid;
// 拼接 Base64 图像数据
verificationCodeImageSrc.value = `data:${imageType};base64,${verificationCodeBase64.value}`;
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 3.2.2 渲染到页面上
<div class="verificationCode-class">
<img :src="verificationCodeImageSrc" alt="验证码加载失败" />
<el-link class="mx-1" @click="updateVerificationCodeImage"
>看不清楚, 换一张</el-link
>
</div>
1
2
3
4
5
6
2
3
4
5
6
# 3.3 前端用户点击登录
登录表单的数据加上 uuid
和验证码,确保唯一性。
const form = reactive({
username: '',
password: '',
uuid: '',
verificationCode: '',
});
1
2
3
4
5
6
2
3
4
5
6
# 3.4 后端验证用户输入的验证码是否正确
@Override
public Result<AppUserLoginVO> userLogin(AppUserLoginDTO appUserLoginDTO) {
if (Objects.nonNull(appUserLoginDTO)) {
// 先对验证码进行核对
ValueOperations<String, String> ops = redisTemplate.opsForValue();
String uuid = appUserLoginDTO.getUuid();
String verificationCode = appUserLoginDTO.getVerificationCode();
String verificationCodeKey = "VerificationCode:" + uuid;
// redis 里面存的验证码
String redisVerificationCode = ops.get(verificationCodeKey);
if (!Objects.equals(verificationCode, redisVerificationCode)) {
// 用户填入的验证码与 redis 中的不相同
return Result.success(new AppUserLoginVO(), "登录失败,验证码错误");
} else {
// 相同
ops.getOperations().delete(verificationCodeKey);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19