欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot使用Captcha生成验证码

程序员文章站 2022-03-30 11:01:46
1. 基本结构使用captcha生成验证码, 利用redis存储验证码redis中的结构为, key是32位的uuid, value为captcha的4位随机字母以及数字的集合设定redis过期时间为...

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. 前端页面的实现

前端结构如图, 实现了一个简单的验证码

SpringBoot使用Captcha生成验证码

<!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使用Captcha生成验证码

以上就是springboot使用captcha生成验证码的详细内容,更多关于springboot生成验证码的资料请关注其它相关文章!