圖形驗證碼組件

hutool的驗證碼已經夠用,但有些地方需要自定義(畫布超界、去除一些字符),所以對此進行了擴展。

依賴

<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>4.5.8</version>
		</dependency>

生成器

package com.rz.util;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.img.GraphicsUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 自定義驗證碼生成器(因原生工具經常出現字符超出畫布邊界,去除一些0和o等字母,特此自定義一個)
 *
 * @author sunziwen
 * @version 1.0
 * @date 2019/9/2 14:24
 **/
public class BuilderCaptcha extends AbstractCaptcha {
    /**
     * 用於隨機選的數字
     */
    private static final String BASE_NUMBER = "123456789";
    /**
     * 用於隨機選的字符
     */
    private static final String BASE_CHAR_LOWER = "abcdefghjkmnpqrstuvwxyz";
    private static final String BASE_CHAR_UP = "ABCDEFGHJKMNPQRSTUVWXYZ";
    /**
     * 用於隨機選的字符和數字
     */
    private static final String BASE_CHAR_NUMBER = BASE_NUMBER + BASE_CHAR_LOWER + BASE_CHAR_UP;

    /**
     * 構造,使用隨機驗證碼生成器生成驗證碼
     *
     * @param width          圖片寬
     * @param height         圖片高
     * @param codeCount      字符個數
     * @param interfereCount 驗證碼干擾元素個數
     */
    public BuilderCaptcha(int width, int height, int codeCount, int interfereCount) {
        super(width, height, new RandomGenerator(BASE_CHAR_NUMBER, codeCount), interfereCount);
    }

    /**
     * 根據生成的code創建驗證碼圖片
     *
     * @param code 驗證碼
     */
    @Override
    protected Image createImage(String code) {
        // 圖像buffer
        final BufferedImage image = new BufferedImage(width + 100, height + 50, BufferedImage.TYPE_INT_RGB);
        final Graphics2D g = GraphicsUtil.createGraphics(image, ObjectUtil.defaultIfNull(this.background, Color.WHITE));

        // 干擾線
        drawInterfere(g);

        // 字符串
        drawString(g, code);

        return image;
    }

    /**
     * 繪製字符串
     *
     * @param g    {@link Graphics2D}畫筆
     * @param code 驗證碼
     */
    private void drawString(Graphics2D g, String code) {
        // 抗鋸齒
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 創建字體
        g.setFont(this.font);

        // 文字
        int minY = GraphicsUtil.getMinY(g);
        if (minY < 0) {
            minY = this.height - 1;
        }

        final int len = this.generator.getLength();
        int charWidth = width / len;
        for (int i = 0; i < len; i++) {
            // 產生隨機的顏色值,讓輸出的每個字符的顏色值都將不同。
            g.setColor(ImgUtil.randomColor());
            g.drawString(String.valueOf(code.charAt(i)), i * charWidth + 50, RandomUtil.randomInt(minY, this.height));
        }
    }

    /**
     * 繪製干擾線
     *
     * @param g {@link Graphics2D}畫筆
     */
    private void drawInterfere(Graphics2D g) {
        final ThreadLocalRandom random = RandomUtil.getRandom();
        // 干擾線
        for (int i = 0; i < this.interfereCount; i++) {
            int xs = random.nextInt(width + 200);
            int ys = random.nextInt(height + 100);
            int xe = xs + random.nextInt(width / 8);
            int ye = ys + random.nextInt(height / 8);
            g.setColor(ImgUtil.randomColor(random));
            g.drawLine(xs, ys, xe, ye);
        }
    }
}

附上一個對應的工具類:

package com.rz.util;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.rz.config.Config;
import com.rz.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;

/**
 * 圖形驗證碼工具類
 *
 * @author sunziwen
 * @version 1.0
 * @date 2019/6/10 10:44
 **/
@Component
public class RzCaptchaUtil {
    private RedisUtil redisUtil;
    private Config config;

    @Autowired
    public RzCaptchaUtil(RedisUtil redisUtil, Config config) {
        this.redisUtil = redisUtil;
        this.config = config;
    }

    /**
     * 獲取圖形驗證碼
     *
     * @return
     */
    public HashMap<String, Object> getCaptchaBase64() {
        BuilderCaptcha lineCaptcha = new BuilderCaptcha(config.getCaptchWidth(), config.getCaptchHeight(), 4, 150);
        String imageBase64 = lineCaptcha.getImageBase64();
        String code = lineCaptcha.getCode();
        HashMap<String, Object> hashMap = new HashMap<>(2);
        hashMap.put("captcha", "data:image/png;base64," + imageBase64);
        //存儲在redis中的key
        String key = IdUtil.randomUUID() + System.currentTimeMillis();
        hashMap.put("verifyStr", key);
        redisUtil.set(key, code, config.getCaptchExpireTime());
        return hashMap;
    }

    /**
     * 校驗圖形驗證碼
     *
     * @param code
     * @param verify
     * @return
     */
    public boolean verifyCaptcha(String code, String verify) {
        String redisCode = redisUtil.get(verify) + "";
        if (StrUtil.isNotBlank(code) && StrUtil.equalsIgnoreCase(redisCode, code)) {
            redisUtil.remove(verify);
            return true;
        }
        return false;
    }
}

使用示例:

@PostMapping(value = "/getCaptcha", name = "獲取圖形驗證碼")
    @ApiOperation("獲取圖形驗證碼")
    public AppResDto getCaptcha() {
        HashMap<String, Object> hashMap = rzCaptchaUtil.getCaptchaBase64();
        return AppResDto.success(hashMap);
    }
@ApiOperation("登錄")
    @PostMapping("/login")
    @Limit(program = "rz-xxxx", prefix = "users-login", count = 10, period = 30, limitType = LimitType.ANNOTATION)
    public AppResDto login(@RequestParam("phone") @LimitParam String phone,
                           @RequestParam("password") String password,
                           @RequestParam("captcha") String captcha,
                           @RequestParam("verifyStr") String verifyStr) {
        //圖形驗證碼正確
        AssertUtil.isTrue(ResEnums.verify_error, rzCaptchaUtil.verifyCaptcha(captcha, verifyStr));
        return userController.login(phone, password);
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章