SpringBoot使用Captcha生成验证码

 更新时间:2021年4月24日 15:01  点击:2417

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生成验证码的资料请关注猪先飞其它相关文章!

[!--infotagslink--]

相关文章

  • 解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28
  • SpringBoot实现excel文件生成和下载

    这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
  • PHP 验证码不显示只有一个小红叉的解决方法

    最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
  • 详解springBoot启动时找不到或无法加载主类解决办法

    这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • JS实现随机生成验证码

    这篇文章主要为大家详细介绍了JS实现随机生成验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
  • jQuery Real Person验证码插件防止表单自动提交

    本文介绍的jQuery插件有点特殊,防自动提交表单的验证工具,就是我们经常用到的验证码工具,先给大家看看效果。效果图如下: 使用说明 需要使用jQuery库文件和Real Person库文件 同时需要自定义验证码显示的CSS样式 使用实例...2015-11-08
  • 解决Springboot get请求是参数过长的情况

    这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
  • Spring Boot项目@RestController使用重定向redirect方式

    这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02
  • Springboot+TCP监听服务器搭建过程图解

    这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
  • springBoot 项目排除数据库启动方式

    这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
  • Jquery插件实现点击获取验证码后60秒内禁止重新获取

    通过jquery.cookie.js插件可以快速实现“点击获取验证码后60秒内禁止重新获取(防刷新)”的功能效果图:先到官网(http://plugins.jquery.com/cookie/)下载cookie插件,放到相应文件夹,代码如下:复制代码 代码如下: <!DOCTYPE ht...2015-03-15
  • php实现点击可刷新验证码

    验证码类文件 CreateImg.class.php <&#63;php class ValidationCode { private $width,$height,$codenum; public $checkcode; //产生的验证码 private $checkimage; //验证码图片 private $disturbColor = ''; /...2015-11-08
  • 详解SpringBoot之访问静态资源(webapp...)

    这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14
  • SpringBoot接口接收json参数解析

    这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
  • springboot中使用@Transactional注解事物不生效的坑

    这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26
  • 基于JavaScript实现验证码功能

    这篇文章主要介绍了基于JavaScript实现验证码功能的相关资料...2017-04-03
  • springboot多模块包扫描问题的解决方法

    这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
  • 单击按钮发送验证码,出现倒计时的简单实例

    下面小编就为大家带来一篇单击按钮发送验证码,出现倒计时的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 代码...2017-07-06
  • Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解

    这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18