SpringBoot使用Captcha生成验证码
使用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的过期时间
#Portserver: port: 80 servlet: session: timeout: 1spring: 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#Mybatismybatis: 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配置@Configurationpublic 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 keyproperties.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对象使用上述配置, 并返回这个BeanDefaultKaptcha 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();//生成tokenMap<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;@Servicepublic 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) {//生成一个tokenString 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中找对应的valueValueOperations<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;@RestControllerpublic 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' placeholder='请输入用户名' name='userName'> <br> <input type='password' placeholder='请输入密码' name='password'> <br> <input type='text' placeholder='请输入验证码' maxlength='4' name='inputCode'> <!--通过隐藏域传递值, 在下面的验证码点击事件中, 将值绑定过来, 这样就可以获得最新的验证码对应的值了!--> <input value='' type='hidden' name='token'> <input type='submit' value='登录'></form> </div> <div><!-- 当用户链接时,void(0)计算为0,用户点击不会发生任何效果 --><a href='javascript:void(0);' rel='external nofollow' > <!--this参数, 返回当前的DOM元素--> <img src='https://www.haobala.com/bcjs/13137.html' alt='更换验证码' 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生成验证码的资料请关注好吧啦网其它相关文章!
相关文章: