SpringBoot -- 画像確認コード kaptcha

この記事では、Kaptcha を SpringBoot に統合する方法と、kaptcha の設定方法、検証コードの生成と検証方法などを紹介します。

序文

参考リンク:

Kaptcha は Google のオープンソースで、非常に強力な機能を備えた自由に構成可能な画像検証コード生成ツールです。Kaptchaを使用すると、画像の幅や高さ、文字の内容、干渉の種類などを設定し、スタイルをカスタマイズできます。

環境構築

完全なプロジェクト、参照https://github.com/miaoyang/spring-learn.git。このプロジェクトには、Redis、MongoDB、Swagger などの他のミドルウェアの使用も含まれています。

プロジェクト構造

ここに画像の説明を挿入
検証コードはマイクロサービスとして別途記述しますが、実際の開発では単一のプロジェクトで部分を切り出すことができます。

依存関係を追加する

pom ファイルに必要な依存関係を導入します。

<?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 設定

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

コード

キャプチャ設定

CheckCodeProperties検証コードのいくつかの基本プロパティが設定されており、これらはapplication.ymlで変更できます。

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

インターフェイス ドキュメントの構成。この部分はKnife4j を統合する SpringBootを参照します。この記事ではオプションですが、インターフェイスをテストする便宜のためです。

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();
    }
}

ドメイン

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;
}

サービス

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;
    }
}

ユーティリティ

キーとコードの生成に使用されます

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();
    }

}

コントローラ

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);
    }

}

テスト

Knfie4j を使用してインターフェイスをテストする
ここに画像の説明を挿入

確認コードを生成する

ここに画像の説明を挿入
対応する値をブラウザーのアドレスにコピーしaliasing、次のように画像を解析します。
ここに画像の説明を挿入

確認コードを確認する

クライアントはキーと検証コード番号を渡す必要があり、サーバーはキーに対応する値をキャッシュから取得し、それをクライアントから渡された検証コードと照合します。
知らせ:

  • キャッシュの時間

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/baidu_33256174/article/details/130332239