基于kaptcha实现后端验证码

实现方式

后端引入kaptcha谷歌验证码生成工具,封装成一个接口。前端调用前先生成一个uuid字符串,将这个参数拼在图片的src属性后边,后端获得这个uuid参数,将其存入以uuid为key值,生成的验证码code值为value存入redis,有效期验证10分钟。校验验证码的时候前端将uuid值和code值一起传到后端,后端通过redis查找校验验证码是否正确。一般需求情况下,验证码需要刷新,因为uuid参数是拼在src属性后边,重新生成一个uuid即可完成刷新。

后端

pom.xml 需要依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 基于baomidou封装过的kaptcha -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>

<!--避免写get,set-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

application.yml 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server:
port: 8080
spring:
application:
name: springboot-kaptcha
#redis配置
redis:
host: localhost
port: 6379
#验证码基础设置
kaptcha:
height: 50
width: 200
content:
length: 4
source: abcdefghjklmnopqrstuvwxyz23456789
space: 2
font:
color: black
name: Arial
size: 40
background-color:
from: lightGray
to: white
border:
enabled: true
color: black
thickness: 1

Redis 工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.example.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* redis操作Service的实现类
*/
@Component
public class RedisUtils {
@Autowired
private StringRedisTemplate stringRedisTemplate;

/**
* 存储数据
*
* @param key 键
* @param value 值
*/
public void set(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}

/**
* 存储数据到指定区域
*
* @param region 区域
* @param key 键
* @param value 值
*/
public void set(String region, String key, String value) {
stringRedisTemplate.opsForValue().set(region + ":" + key, value);
}

/**
* 存储数据并设置时间
*
* @param region 区域
* @param key 键
* @param value 值
* @param expire 过期时间
*/
public void set(String region, String key, String value, long expire) {
String rKey = region + ":" + key;
stringRedisTemplate.opsForValue().set(rKey, value);
stringRedisTemplate.expire(rKey, expire, TimeUnit.SECONDS);
}

/**
* 获取数据
*
* @param key 键
* @return
*/
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}

/**
* @Description: 通过区域和键获取数据
* @Author: zhengyue
* @Date: 2020/8/24 15:04
* @Param: [region, key]
* @Return: java.lang.String
*/
public String get(String region, String key) {
return stringRedisTemplate.opsForValue().get(region + ":" + key);
}

/**
* 设置超期时间
*
* @param key 键
* @param expire 过期时间
* @return 是否成功
*/
public boolean expire(String key, long expire) {
return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
}

/**
* 设置区域超期时间
*
* @param key 键
* @param expire 过期时间
* @return 是否成功
*/
public boolean expire(String region, String key, long expire) {
return stringRedisTemplate.expire(region+":"+key, expire, TimeUnit.SECONDS);
}

/**
* 删除数据
*
* @param key 键
*/
public void remove(String key) {
stringRedisTemplate.delete(key);
}

/**
* 删除区域数据
* @Param region 区域
* @param key 键
*/
public void remove(String region, String key) {
stringRedisTemplate.delete(region + ":" +key);
}

/**
* @Description: 删除指定区域的数据
* @Author: zhengyue
* @Date: 2020/8/24 14:49
* @Param: region
* @Return:
*/
public void removeRegion(String region) {
Set<String> keys = stringRedisTemplate.keys(region + ":*");
stringRedisTemplate.delete(keys);
}

/**
* 自增操作
*
* @param key 键
* @param delta 自增步长
* @return
*/
public Long increment(String key, long delta) {
return stringRedisTemplate.opsForValue().increment(key, delta);
}
}

生成验证码接口

从前端获取一个uuid,将uuid和生成的code值以key、value形式存入redis,并设置有效期限。同时返回验证码图片给前端。

1
2
3
4
5
6
7
8
@GetMapping("/render")
public void render(String uuid) {
String code = kaptcha.render();
// 存入redis
redisUtils.set(uuid, code);
// 设定验证码过期时间900s
redisUtils.expire(uuid, 60*10);
}

校验验证码

从前端获取uuid以及code值,通过redis查找code值是否匹配,匹配成功移除rediskey值。

1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping("/valid")
public Boolean validDefaultTime(KaptchaValidDTO dto) {
String code = null;
code = redisUtils.get(dto.getUuid());
// 没有找到key值,或者不等于传递过来的key值,返回验证失败
if(StringUtils.isEmpty(code)
|| !code.equals(dto.getCode())) {
return false;
}
// 验证成功,移除key值
redisUtils.remove(dto.getUuid());
return true;
}

前端

验证码获取

前端是基于vue实现的,创建一个img标签用来接收后端生成接口返回的图片,这里还需要有一个uuid。在vue的mounted生命周期方法中先生成uuid值,并拼到图片的src属性后边。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<div class="kaptcha">
<img :src="`http://localhost:8080/kaptcha/render?uuid=` + uuid" alt="" />
</div>
</template>
<script>
export default {
data() {
return {
uuid: "",
};
},
mounted() {
this.uuid = this.uuidRender()
},
methods: {
// 生成uuid
uuidRender() {
var temp_url = URL.createObjectURL(new Blob())
var uuid = temp_url.toString()
URL.revokeObjectURL(temp_url)
return uuid.substr(uuid.lastIndexOf("/") + 1)
}
},
};
</script>

页面成功显示图片

刷新二维码

给img绑定一个点击事件,执行refresh方法

1
<img @click="refresh" :src="`http://localhost:8080/kaptcha/render?uuid=` + uuid" alt="" />

通过重新生成uuid来刷新图片链接地址,从而重新请求

1
2
3
4
// 刷新二维码
refresh() {
this.uuid = this.uuidRender()
}
如果你觉得有帮助,慷慨如你,可以扫描下面的二维码赞赏一下