SpringBoot使用Captcha生成驗(yàn)證碼
1. 基本結(jié)構(gòu)
使用Captcha生成驗(yàn)證碼, 利用Redis存儲(chǔ)驗(yàn)證碼
Redis中的結(jié)構(gòu)為, Key是32位的UUID, Value為Captcha的4位隨機(jī)字母以及數(shù)字的集合
設(shè)定Redis過(guò)期時(shí)間為1min, 即可實(shí)現(xiàn)過(guò)期驗(yàn)證碼的自動(dòng)失效
2. Kaptcha的依賴(lài)
基本的依賴(lài)這里不再敘述, 主要說(shuō)一下要導(dǎo)入Captcha的依賴(lài)
<!--Kaptcha-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
所有的依賴(lài)如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wang</groupId>
<artifactId>spring_security_framework</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_security_framework</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--SpringSecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--SpringSecurity with thymeleaf-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!--MySQL connector-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency>
<!--FastJSON-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--Swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!--HuTool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.7</version>
</dependency>
<!--Kaptcha-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. 配置SpringBoot
配置SpringBoot的配置文件, 這里主要關(guān)注一個(gè)session的過(guò)期時(shí)間
#Port
server:
port: 80
servlet:
session:
timeout: 1
spring:
application:
name: SpringSecurityFramework
#dataBase Setting
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Druid Setting
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
#Setting For Druid StatView and Filter
filters: stat,wall,log4j
max-pool-prepared-statement-per-connection-size: 20
use-global-data-source-stat: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSql
#Redis Setting
redis:
host: 127.0.0.1
port: 6379
#Thymeleaf
thymeleaf:
cache: false
#Mybatis
mybatis:
type-aliases-package: com.wang.entity
mapper-locations: classpath:Mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
其余的配置, 如log4j, druid, SpringSecurity, RedisTemplate,這里就不再贅述
4. 配置Captcha
我們可以通過(guò)JAVA的配置類(lèi)來(lái)配置Captcha生成驗(yàn)證碼的一些規(guī)則
package com.wang.spring_security_framework.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
//Kaptcha配置
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer() {
//Properties類(lèi)
Properties properties = new Properties();
// 圖片邊框
properties.setProperty("kaptcha.border", "yes");
// 邊框顏色
properties.setProperty("kaptcha.border.color", "105,179,90");
// 字體顏色
properties.setProperty("kaptcha.textproducer.font.color", "blue");
// 圖片寬
properties.setProperty("kaptcha.image.width", "110");
// 圖片高
properties.setProperty("kaptcha.image.height", "40");
// 字體大小
properties.setProperty("kaptcha.textproducer.font.size", "30");
// session key
properties.setProperty("kaptcha.session.key", "code");
// 驗(yàn)證碼長(zhǎng)度
properties.setProperty("kaptcha.textproducer.char.length", "4");
// 字體
properties.setProperty("kaptcha.textproducer.font.names", "宋體,楷體,微軟雅黑");
//圖片干擾
properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.DefaultNoise");
//Kaptcha 使用上述配置
Config config = new Config(properties);
//DefaultKaptcha對(duì)象使用上述配置, 并返回這個(gè)Bean
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
5. 工具類(lèi)
使用UUID作為key, 同時(shí)考慮到對(duì)驗(yàn)證碼的輸出結(jié)果可能有不同的要求, 這里寫(xiě)兩個(gè)工具類(lèi)來(lái)處理它們
UUIDUtil
package com.wang.spring_security_framework.util;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component public class UUIDUtil {
/** * 生成32位的隨機(jī)UUID * @return 字符形式的小寫(xiě)UUID */
@Bean public String getUUID32() {
return UUID.randomUUID().toString() .replace("-", "").toLowerCase();
}
}
CaptchaUtil
package com.wang.spring_security_framework.util;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.wang.spring_security_framework.service.CaptchaService;
import io.netty.handler.codec.base64.Base64Encoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
@Component
//Captcha 生成工具
public class CaptchaUtil {
@Autowired
private DefaultKaptcha producer;
@Autowired
private CaptchaService captchaService;
//生成catchCreator的map
public Map<String, Object> catchaImgCreator() throws IOException {
//生成文字驗(yàn)證碼
String text = producer.createText();
//生成文字對(duì)應(yīng)的圖片驗(yàn)證碼
BufferedImage image = producer.createImage(text);
//將圖片寫(xiě)出
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", outputStream);
//對(duì)寫(xiě)出的字節(jié)數(shù)組進(jìn)行Base64編碼 ==> 用于傳遞8比特字節(jié)碼
BASE64Encoder encoder = new BASE64Encoder();
//生成token
Map<String, Object> token = captchaService.createToken(text);
token.put("img", encoder.encode(outputStream.toByteArray()));
return token;
}
}
6. 接口以及實(shí)現(xiàn)類(lèi)
1. 接口
package com.wang.spring_security_framework.service;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Map;
public interface CaptchaService {
//生成token
Map<String, Object> createToken(String captcha);
//生成captcha驗(yàn)證碼
Map<String, Object> captchaCreator() throws IOException;
//驗(yàn)證輸入的驗(yàn)證碼是否正確
String versifyCaptcha (String token, String inputCode);
}
2. 實(shí)現(xiàn)類(lèi)
package com.wang.spring_security_framework.service.serviceImpl;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.wang.spring_security_framework.service.CaptchaService;
import com.wang.spring_security_framework.util.CaptchaUtil;
import com.wang.spring_security_framework.util.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class CaptchaServiceImpl implements CaptchaService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UUIDUtil uuidUtil;
@Autowired
private CaptchaUtil captchaUtil;
//從SpringBoot的配置文件中取出過(guò)期時(shí)間
@Value("${server.servlet.session.timeout}")
private Integer timeout;
//UUID為key, 驗(yàn)證碼為Value放在Redis中
@Override
public Map<String, Object> createToken(String captcha) {
//生成一個(gè)token
String key = uuidUtil.getUUID32();
//生成驗(yàn)證碼對(duì)應(yīng)的token 以token為key 驗(yàn)證碼為value存在redis中
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, captcha);
//設(shè)置驗(yàn)證碼過(guò)期時(shí)間
redisTemplate.expire(key, timeout, TimeUnit.MINUTES);
Map<String, Object> map = new HashMap<>();
map.put("token", key);
map.put("expire", timeout);
return map;
}
//生成captcha驗(yàn)證碼
@Override
public Map<String, Object> captchaCreator() throws IOException {
return captchaUtil.catchaImgCreator();
}
//驗(yàn)證輸入的驗(yàn)證碼是否正確
@Override
public String versifyCaptcha(String token, String inputCode) {
//根據(jù)前端傳回的token在redis中找對(duì)應(yīng)的value
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
if (redisTemplate.hasKey(token)) {
//驗(yàn)證通過(guò), 刪除對(duì)應(yīng)的key
if (valueOperations.get(token).equals(inputCode)) {
redisTemplate.delete(token);
return "true";
} else {
return "false";
}
} else {
return "false";
}
}
}
- 這里的驗(yàn)證, 只是簡(jiǎn)單的驗(yàn)證了輸入是否能從Redis中匹配, 返回了字符串
- 真實(shí)的驗(yàn)證中, 我們還要在邏輯中添加用戶(hù)名和密碼的考慮
7. Controller
package com.wang.spring_security_framework.controller;
import com.wang.spring_security_framework.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
@RestController
public class LoginController {
@Autowired
CaptchaService captchaService;
@GetMapping("/captcha")
public Map<String, Object> captcha() throws IOException {
return captchaService.captchaCreator();
}
@GetMapping("/login1")
public String login(@RequestParam("token") String token,
@RequestParam("inputCode") String inputCode) {
return captchaService.versifyCaptcha(token, inputCode);
}
}
- captcha 用于獲取一個(gè)驗(yàn)證碼
- login1 用于接收到前端的請(qǐng)求后驗(yàn)證并返回結(jié)果
- login1 這里為了測(cè)試簡(jiǎn)便實(shí)用了GET方法, 而實(shí)際中最好使用POST方法, 這樣安全性更高
8. 前端頁(yè)面的實(shí)現(xiàn)
前端結(jié)構(gòu)如圖, 實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的驗(yàn)證碼

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登錄</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<div>
<div>
<form th:action="@{/login1}" method="get">
<input type="text" id="userName" placeholder="請(qǐng)輸入用戶(hù)名" name="userName">
<br>
<input type="password" id="password" placeholder="請(qǐng)輸入密碼" name="password">
<br>
<input type="text" id="inputCode" placeholder="請(qǐng)輸入驗(yàn)證碼" maxlength="4" name="inputCode">
<!--通過(guò)隱藏域傳遞值, 在下面的驗(yàn)證碼點(diǎn)擊事件中, 將值綁定過(guò)來(lái), 這樣就可以獲得最新的驗(yàn)證碼對(duì)應(yīng)的值了!-->
<input id="token" value="" type="hidden" name="token">
<input type="submit" value="登錄">
</form>
</div>
<div>
<!-- 當(dāng)用戶(hù)鏈接時(shí),void(0)計(jì)算為0,用戶(hù)點(diǎn)擊不會(huì)發(fā)生任何效果 -->
<a href="javascript:void(0);" rel="external nofollow" title="點(diǎn)擊更換驗(yàn)證碼">
<!--this參數(shù), 返回當(dāng)前的DOM元素-->
<img src="" alt="更換驗(yàn)證碼" id="imgVerify" onclick="getVerify(this)">
</a>
</div>
</div>
<script>
//獲得img對(duì)象
let imgVerify = $("#imgVerify").get(0);
//$(function())等同于$(document).ready(function()) ==> 頁(yè)面加載完畢之后, 才執(zhí)行函數(shù)
$(function () {
getVerify(imgVerify);
});
//onclick時(shí)間綁定的getVerify函數(shù)
function getVerify(obj) {
$.ajax({
type: "GET",
url: "/captcha",
success: function (result) {
obj.src = "data:image/jpeg;base64," + result.img;
$("#token").val(result.token);
}
});
}
</script>
</body>
</html>
- 用一個(gè) a 標(biāo)簽包圍 img 標(biāo)簽, 這樣如果圖片沒(méi)有加載出來(lái)也有一個(gè)超鏈接, 不過(guò)點(diǎn)了以后沒(méi)有效果
- (function())等同于(function())等同于(document).ready(function()) ==> 頁(yè)面加載完畢之后, 才執(zhí)行函數(shù), 這里必須要寫(xiě)這個(gè)函數(shù), 否則第一次加載不會(huì)調(diào)用 onclick 方法, 也就不會(huì)生成驗(yàn)證碼!
- 我們利用隱藏域?qū)Ⅱ?yàn)證碼的key傳遞到表單中, 我們?cè)?img 的點(diǎn)擊事件對(duì)應(yīng)的函數(shù)的ajax回調(diào)函數(shù)中可以利用jQuery操作DOM, 順帶取出key值放到我們的隱藏域中, 這樣提交的時(shí)候就會(huì)提交 key 和用戶(hù)輸入的 value 了
示例

驗(yàn)證通過(guò)

以上就是SpringBoot使用Captcha生成驗(yàn)證碼的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot生成驗(yàn)證碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Springboot通過(guò)谷歌Kaptcha?組件生成圖形驗(yàn)證碼功能
- springboot驗(yàn)證碼生成以及驗(yàn)證功能舉例詳解
- SpringBoot使用Kaptcha實(shí)現(xiàn)驗(yàn)證碼的生成與驗(yàn)證功能
- springboot驗(yàn)證碼的生成與驗(yàn)證的兩種方法
- SpringBoot 圖形驗(yàn)證碼的生成和校驗(yàn)
- SpringBoot實(shí)現(xiàn)Thymeleaf驗(yàn)證碼生成
- springboot整合kaptcha生成驗(yàn)證碼功能
- springboot控制層圖片驗(yàn)證碼生成
- SpringBoot實(shí)現(xiàn)前端驗(yàn)證碼圖片生成和校驗(yàn)
- SpringBoot使用hutool-captcha實(shí)現(xiàn)驗(yàn)證碼生成與驗(yàn)證
相關(guān)文章
java網(wǎng)上圖書(shū)商城(3)Book模塊
這篇文章主要為大家詳細(xì)介紹了java網(wǎng)上圖書(shū)商城,Book模塊,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
詳解SpringBoot中關(guān)于%2e的Trick
這篇文章主要介紹了SpringBoot中關(guān)于%2e的Trick,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
SpringBoot整合Lettuce redis過(guò)程解析
這篇文章主要介紹了SpringBoot整合Lettuce redis過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
MybatisPlus中@TableField注解的使用詳解
這篇文章主要介紹了MybatisPlus中@TableField注解的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
ArrayList源碼探秘之Java動(dòng)態(tài)數(shù)組的實(shí)現(xiàn)
這篇文章將帶大家從ArrayList源碼來(lái)探秘一下Java動(dòng)態(tài)數(shù)組的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),對(duì)我們深入了解JavaScript有一定的幫助,需要的可以參考一下2023-08-08

