SpringBoot--picture verification code kaptcha

This article introduces how to integrate kaptcha in SpringBoot, and how to configure kaptcha, generate verification code and verification, etc.

foreword

Reference link:

Kaptcha is a Google open source, freely configurable image verification code generation tool with very powerful functions. When using Kaptcha, you can configure the width and height of the picture, character content, interference type, etc., and customize the style.

Environment build

Complete project, reference https://github.com/miaoyang/spring-learn.git, this project also includes the use of some other middleware, such as Redis, MongoDB, Swagger, etc.

project structure

insert image description here
The verification code is written as a microservice separately. In actual development, the single project can cut parts.

add dependencies

Introduce the necessary dependencies in the pom file

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-learn</artifactId>
        <groupId>org.ym</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>learn-checkcode</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--check code-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.ym</groupId>
            <artifactId>learn-common-core</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.ym</groupId>
            <artifactId>learn-common-swagger</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.ym</groupId>
            <artifactId>learn-common-redis</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

application.yml configuration

server:
  port: 9205
spring:
  application:
    name: learn-checkcode
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  redis:
    host: localhost # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password:  # Redis服务器连接密码(默认为空)
    timeout: 3000ms # 连接超时时间(毫秒)

checkcode:
  length: 4
  prefix-key: check_code_
  expire-time: 300 # 300s

Code

KaptchaConfig

CheckCodePropertiesSome basic properties of the verification code are configured, which application.ymlcan be modified in .

package com.ym.learn.checkcode.config;

import lombok.Data;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 19:44
 * @Desc:
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "checkcode")
public class CheckCodeProperties {
    
    
    /**
     * 验证码长度
     */
    private Integer length;
    /**
     * key前缀
     */
    private String prefixKey;
    /**
     * 缓存过期时间
     */
    private Integer expireTime;

}

package com.ym.learn.checkcode.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;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 16:25
 * @Desc: 验证码配置
 */
@Configuration
public class KaptchaConfig {
    
    
    @Bean
    public DefaultKaptcha defaultKaptcha() {
    
    
        Properties properties = new Properties();
        properties.put("kaptcha.border", "no");

        // 文本宽度和长度
        properties.put("kaptcha.textproducer.char.space", "10");
        properties.put("kaptcha.textproducer.char.length","4");
        // 宽度和高度
        properties.put("kaptcha.image.height","34");
        properties.put("kaptcha.image.width","130");
        // 字体大小和颜色
        properties.put("kaptcha.textproducer.font.size","25");
        properties.put("kaptcha.textproducer.font.color", "black");
        // 背景
        properties.setProperty("kaptcha.background.clear.from", "white");
        properties.setProperty("kaptcha.background.clear.to", "white");

        properties.put("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");

        Config config = new Config(properties);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

Knife4jConfig

Interface document configuration, this part refers to SpringBoot integrating Knife4j , in this article, it is optional, but for the convenience of testing the interface.

package com.ym.learn.checkcode.config;

import com.ym.learn.swagger.config.BaseKnife4jConfig;
import com.ym.learn.swagger.domain.Knife4jProperties;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.oas.annotations.EnableOpenApi;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 22:07
 * @Desc:
 */
@Configuration
@EnableOpenApi
public class Knife4jConfig extends BaseKnife4jConfig {
    
    
    @Override
    public Knife4jProperties knife4jProperties() {
    
    
        return Knife4jProperties.builder()
                .apiBasePackage("com.ym.learn.checkcode")
                .title("验证码服务接口文档")
                .description("验证码服务接口文档")
                .contactName("ym")
                .version("1.0")
                .enableSecurity(false)
                .build();
    }
}

domain

package com.ym.learn.checkcode.domain;

import lombok.Builder;
import lombok.Data;
import lombok.ToString;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 17:36
 * @Desc: 返回给客户端的Code
 */
@Data
@ToString
@Builder
public class CodeVo {
    
    
    /**
     * 用于验证的key
     */
    private String key;
    /**
     * 校验码
     */
    private String aliasing;
}

Service

package com.ym.learn.checkcode.service;

import com.ym.learn.checkcode.domain.CodeVo;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 17:32
 * @Desc:
 */
public interface CheckCodeService {
    
    

    /**
     * 校验验证码
     * @param key
     * @param code
     * @return
     */
    boolean verifyCode(String key, String code);

    /**
     * 生成验证码
     * @param length
     * @return
     */
    CodeVo generateCode(Integer length, String prefixKey,Integer expireTime);
}

package com.ym.learn.checkcode.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import com.ym.learn.checkcode.domain.CodeVo;
import com.ym.learn.checkcode.service.CheckCodeService;
import com.ym.learn.checkcode.util.CodeUtil;
import com.ym.learn.core.utils.SignUtil;
import com.ym.learn.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 17:33
 * @Desc:
 */
@Service
@Slf4j
public class CheckCodeServiceImpl implements CheckCodeService {
    
    
    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private RedisService redisService;

    @Override
    public boolean verifyCode(String key, String code) {
    
    
        if (StrUtil.isEmpty(key) || StrUtil.isEmpty(code)){
    
    
            return false;
        }
        String localCode = (String)redisService.get(key);
        if (StrUtil.isEmpty(localCode)){
    
    
            return false;
        }
        if (!code.equalsIgnoreCase(localCode)){
    
    
            return false;
        }
        // 删除缓存
        redisService.del(key);
        return true;
    }

    @Override
    public CodeVo generateCode(Integer length, String prefixKey, Integer expireTime) {
    
    
        String code = CodeUtil.generateCode(length);
        String key = CodeUtil.generateKey(prefixKey);
        // 缓存redis
        redisService.set(key,code,expireTime);
        // 获取base64编码后的img
        String codeImg = generateCodeImg(code);
        return CodeVo.builder()
                .key(key)
                .aliasing(codeImg)
                .build();
    }

    /**
     * 生成codeImage
     * @param code
     * @return
     */
    private String generateCodeImg(String code){
    
    
        BufferedImage bufferedImage = defaultKaptcha.createImage(code);
        ByteOutputStream byteOutputStream = null;
        String codeImg = "";
        try {
    
    
            byteOutputStream = new ByteOutputStream();
            ImageIO.write(bufferedImage,"png",byteOutputStream);
            codeImg = "data:image/png;base64,"+SignUtil.encodeBase64(byteOutputStream.getBytes());
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            if (byteOutputStream != null) {
    
    
                byteOutputStream.close();
            }
        }
        return codeImg;
    }
}

util

Used to generate Key and Code

package com.ym.learn.checkcode.util;

import java.util.Random;
import java.util.UUID;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 17:05
 * @Desc: 验证码工具类
 */
public class CodeUtil {
    
    
    /**
     * Code字符的取值范围
     */
    public static final String CODE_NUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    /**
     * 生成UUID,包括前缀
     * @param prefix
     * @return
     */
    public static String generateKey(String prefix){
    
    
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        return prefix+uuid;
    }

    /**
     * 生成指定长度的code
     * @param len 不指定时,默认为4
     * @return
     */
    public static String generateCode(Integer len){
    
    
        if (len <= 0){
    
    
            len = 4;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
    
    
            Random random = new Random();
            int nextInt = random.nextInt(CODE_NUM.length());
            sb.append(CODE_NUM.charAt(nextInt));
        }
        return sb.toString();
    }

}

controller

package com.ym.learn.checkcode.controller;

import com.ym.learn.checkcode.config.CheckCodeProperties;
import com.ym.learn.checkcode.domain.CodeVo;
import com.ym.learn.checkcode.service.CheckCodeService;
import com.ym.learn.core.api.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: Yangmiao
 * @Date: 2023/4/21 17:29
 * @Desc: 验证码接口
 */
@Api(tags = "验证码接口")
@RestController
@RequestMapping("/checkcode")
public class CheckCodeController {
    
    

    @Autowired
    private CheckCodeService checkCodeService;

    @Autowired
    private CheckCodeProperties codeProperties;

    @ApiOperation(value = "获取验证码")
    @GetMapping("/getCheckCode")
    public R getCheckCode(){
    
    
        CodeVo codeVo = checkCodeService.generateCode(codeProperties.getLength(), codeProperties.getPrefixKey(), codeProperties.getExpireTime());
        return R.ok(codeVo);
    }

    @ApiOperation(value = "校验验证码")
    @PostMapping("/verifyCheckCode")
    public R verifyCheckCode(@ApiParam(name = "key")@RequestParam("key")String key,
                             @ApiParam(name = "code")@RequestParam("code")String code){
    
    
        boolean ret = checkCodeService.verifyCode(key, code);
        return R.ok(ret);
    }

}

test

Use Knfie4j to test the interface
insert image description here

generate verification code

insert image description here
Copy aliasingthe corresponding value to the browser address, and parse the picture as follows:
insert image description here

Verify verification code

The client needs to pass in the key and verification code number, and the server fetches the value corresponding to the key from the cache, and matches it with the verification code passed in by the client.
Notice:

  • cache time

insert image description here

Guess you like

Origin blog.csdn.net/baidu_33256174/article/details/130332239