SpringBoot使用Captcha生成驗證碼
1. 基本結構
使用Captcha生成驗證碼, 利用Redis存儲驗證碼
Redis中的結構為, Key是32位的UUID, Value為Captcha的4位隨機字母以及數字的集合
設定Redis過期時間為1min, 即可實現過期驗證碼的自動失效
2. Kaptcha的依賴
基本的依賴這裡不再敘述, 主要說一下要導入Captcha的依賴
<!--Kaptcha--> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>
所有的依賴如下
<?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的配置文件, 這裡主要關註一個session的過期時間
#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
我們可以通過JAVA的配置類來配置Captcha生成驗證碼的一些規則
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類 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"); // 驗證碼長度 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對象使用上述配置, 並返回這個Bean DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
5. 工具類
使用UUID作為key, 同時考慮到對驗證碼的輸出結果可能有不同的要求, 這裡寫兩個工具類來處理它們
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位的隨機UUID * @return 字符形式的小寫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 { //生成文字驗證碼 String text = producer.createText(); //生成文字對應的圖片驗證碼 BufferedImage image = producer.createImage(text); //將圖片寫出 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); //對寫出的字節數組進行Base64編碼 ==> 用於傳遞8比特字節碼 BASE64Encoder encoder = new BASE64Encoder(); //生成token Map<String, Object> token = captchaService.createToken(text); token.put("img", encoder.encode(outputStream.toByteArray())); return token; } }
6. 接口以及實現類
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驗證碼 Map<String, Object> captchaCreator() throws IOException; //驗證輸入的驗證碼是否正確 String versifyCaptcha (String token, String inputCode); }
2. 實現類
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的配置文件中取出過期時間 @Value("${server.servlet.session.timeout}") private Integer timeout; //UUID為key, 驗證碼為Value放在Redis中 @Override public Map<String, Object> createToken(String captcha) { //生成一個token String key = uuidUtil.getUUID32(); //生成驗證碼對應的token 以token為key 驗證碼為value存在redis中 ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); valueOperations.set(key, captcha); //設置驗證碼過期時間 redisTemplate.expire(key, timeout, TimeUnit.MINUTES); Map<String, Object> map = new HashMap<>(); map.put("token", key); map.put("expire", timeout); return map; } //生成captcha驗證碼 @Override public Map<String, Object> captchaCreator() throws IOException { return captchaUtil.catchaImgCreator(); } //驗證輸入的驗證碼是否正確 @Override public String versifyCaptcha(String token, String inputCode) { //根據前端傳回的token在redis中找對應的value ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); if (redisTemplate.hasKey(token)) { //驗證通過, 刪除對應的key if (valueOperations.get(token).equals(inputCode)) { redisTemplate.delete(token); return "true"; } else { return "false"; } } else { return "false"; } } }
- 這裡的驗證, 隻是簡單的驗證瞭輸入是否能從Redis中匹配, 返回瞭字符串
- 真實的驗證中, 我們還要在邏輯中添加用戶名和密碼的考慮
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 用於獲取一個驗證碼
- login1 用於接收到前端的請求後驗證並返回結果
- login1 這裡為瞭測試簡便實用瞭GET方法, 而實際中最好使用POST方法, 這樣安全性更高
8. 前端頁面的實現
前端結構如圖, 實現瞭一個簡單的驗證碼
<!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="請輸入用戶名" name="userName"> <br> <input type="password" id="password" placeholder="請輸入密碼" name="password"> <br> <input type="text" id="inputCode" placeholder="請輸入驗證碼" maxlength="4" name="inputCode"> <!--通過隱藏域傳遞值, 在下面的驗證碼點擊事件中, 將值綁定過來, 這樣就可以獲得最新的驗證碼對應的值瞭!--> <input id="token" value="" type="hidden" name="token"> <input type="submit" value="登錄"> </form> </div> <div> <!-- 當用戶鏈接時,void(0)計算為0,用戶點擊不會發生任何效果 --> <a href="javascript:void(0);" rel="external nofollow" title="點擊更換驗證碼"> <!--this參數, 返回當前的DOM元素--> <img src="" alt="更換驗證碼" id="imgVerify" onclick="getVerify(this)"> </a> </div> </div> <script> //獲得img對象 let imgVerify = $("#imgVerify").get(0); //$(function())等同於$(document).ready(function()) ==> 頁面加載完畢之後, 才執行函數 $(function () { getVerify(imgVerify); }); //onclick時間綁定的getVerify函數 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>
- 用一個 a 標簽包圍 img 標簽, 這樣如果圖片沒有加載出來也有一個超鏈接, 不過點瞭以後沒有效果
- (function())等同於(function())等同於(document).ready(function()) ==> 頁面加載完畢之後, 才執行函數, 這裡必須要寫這個函數, 否則第一次加載不會調用 onclick 方法, 也就不會生成驗證碼!
- 我們利用隱藏域將驗證碼的key傳遞到表單中, 我們在 img 的點擊事件對應的函數的ajax回調函數中可以利用jQuery操作DOM, 順帶取出key值放到我們的隱藏域中, 這樣提交的時候就會提交 key 和用戶輸入的 value 瞭
示例
驗證通過
以上就是SpringBoot使用Captcha生成驗證碼的詳細內容,更多關於SpringBoot生成驗證碼的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- vue+springboot實現登錄驗證碼
- 詳解Spring容器的使用流程
- springboot使用nacos的示例詳解
- java實現登錄驗證碼功能
- 五分鐘解鎖springboot admin監控新技巧