SpringBoot项目实现滑块验证码功能

一.application.yml配置文件 

captcha:
  #是否开启验证码功能,false表示开启
  disabled: false
  #滑块验证码误差,数值越大,允许的误差范围越大
  match-precision: 6
  #生成验证码的图片列表
  image-paths:
    - D:/testFiles/one.jpg

二.配置类和工具类

1.读取配置文件类

package com.example.verificationcode.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "captcha")
public class SliderCaptchaConfig {
    private boolean disabled;
    private int matchPrecision;
    private String[] imagePaths;
}

2.工具类

2.1滑块验证码类

package com.example.verificationcode.utils;

import org.apache.tomcat.util.codec.binary.Base64;

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

public class SliderCaptcha {
    private final BufferedImage image;
    private final BufferedImage slider;
    private final int sliderCoordinateX;
    private final int sliderCoordinateY;

    SliderCaptcha(BufferedImage image, BufferedImage slider, int sliderCoordinateX, int sliderCoordinateY) {
        this.image = image;
        this.slider = slider;
        this.sliderCoordinateX = sliderCoordinateX;
        this.sliderCoordinateY = sliderCoordinateY;
    }

    public BufferedImage getImage() {
        return this.image;
    }

    public String getImageDataUrl() {
        return this.toDataUrl(this.image, "jpeg");
    }

    public int getImageWidth() {
        return this.image.getWidth();
    }

    public int getImageHeight() {
        return this.image.getHeight();
    }

    public BufferedImage getSlider() {
        return this.slider;
    }

    public String getSliderDataUrl() {
        return this.toDataUrl(this.slider, "png");
    }

    public int getSliderWidth() {
        return this.slider.getWidth();
    }

    public int getSliderHeight() {
        return this.slider.getHeight();
    }

    public int getSliderCoordinateX() {
        return this.sliderCoordinateX;
    }

    public int getSliderCoordinateY() {
        return this.sliderCoordinateY;
    }

    private String toDataUrl(BufferedImage image, String type) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();

        try {
            ImageIO.write(image, type, stream);
        } catch (IOException var5) {
            throw new SliderCaptchaException(var5);
        }

        String base64 = Base64.encodeBase64String(stream.toByteArray());
        return String.format("data:image/%s;base64,%s", type, base64);
    }
}

2.2自定义异常类

package com.example.verificationcode.utils;

public class SliderCaptchaException extends RuntimeException {
    public SliderCaptchaException(String message) {
        super(message);
    }

    public SliderCaptchaException(Exception e) {
        super(e);
    }
}

2.3生成验证码工具类

package com.example.verificationcode.utils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;

public class SliderCaptchaGenerator {
    private final List<BufferedImage> originalImages;
    private final List<BufferedImage> sliderOverlayImages;
    private final List<BufferedImage> sliderBorderImages;
    private final Random random;

    public SliderCaptchaGenerator() {
        this(0);
    }

    public SliderCaptchaGenerator(int sliderSideLength) {
        this.random = new Random();
        this.originalImages = new ArrayList();
        BufferedImage sliderOverlay = this.readImage("iVBORw0KGgoAAAANSUhEUgAAAG4AAABuCAYAAADGWyb7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjE4Qjg4MkVDMTQxMjExRUJBOTMyREFCQzAyNENFRTc0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjE4Qjg4MkVEMTQxMjExRUJBOTMyREFCQzAyNENFRTc0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MThCODgyRUExNDEyMTFFQkE5MzJEQUJDMDI0Q0VFNzQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MThCODgyRUIxNDEyMTFFQkE5MzJEQUJDMDI0Q0VFNzQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz71V8U+AAAKbklEQVR42uyd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");
        BufferedImage sliderBorder = this.readImage("iVBORw0KGgoAAAANSUhEUgAAAG4AAABuCAYAAADGWyb7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjAxRTMxMUFCMTQxMjExRUJBNTBDQUREQzgyQUY0QjMwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjAxRTMxMUFDMTQxMjExRUJBNTBDQUREQzgyQUY0QjMwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDFFMzExQTkxNDEyMTFFQkE1MENBRERDODJBRjRCMzAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDFFMzExQUExNDEyMTFFQkE1MENBRERDODJBRjRCMzAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4EDCUuAAAYI0lEQVR42uxd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");
        if (sliderSideLength > 0) {
            sliderOverlay = this.scale(sliderOverlay, sliderSideLength);
            sliderBorder = this.scale(sliderBorder, sliderSideLength);
        }

        this.sliderOverlayImages = List.of(sliderOverlay, this.rotate(sliderOverlay, 90), this.rotate(sliderOverlay, 180), this.rotate(sliderOverlay, 270));
        this.sliderBorderImages = List.of(sliderBorder, this.rotate(sliderBorder, 90), this.rotate(sliderBorder, 180), this.rotate(sliderBorder, 270));
    }

    public void addImage(String path) {
        try {
            this.originalImages.add(ImageIO.read(new File(path)));
        } catch (IOException var3) {
            throw new SliderCaptchaException(var3);
        }
    }

    public SliderCaptcha newOne() {
        if (this.originalImages.size() == 0) {
            throw new SliderCaptchaException("no images");
        } else {
            int randomIndex = this.random.nextInt(0, this.originalImages.size());
            BufferedImage image = this.deepCopy((BufferedImage)this.originalImages.get(randomIndex));
            int imageWidth = image.getWidth();
            int imageHeight = image.getHeight();
            randomIndex = this.random.nextInt(0, this.sliderBorderImages.size());
            BufferedImage sliderOverlay = (BufferedImage)this.sliderOverlayImages.get(randomIndex);
            BufferedImage sliderBorder = (BufferedImage)this.sliderBorderImages.get(randomIndex);
            int sliderWidth = sliderOverlay.getWidth();
            int sliderHeight = sliderOverlay.getHeight();
            int x = this.random.nextInt(Math.max(imageWidth / 3, sliderWidth + 10), imageWidth - sliderWidth - 5);
            int y = this.random.nextInt(5, imageHeight - sliderHeight - 5);
            BufferedImage slider = this.cutImage(image, sliderOverlay, x, y);
            this.overlayImage(image, sliderOverlay, x, y);
            this.overlayImage(slider, sliderBorder, 0, 0);
            return new SliderCaptcha(image, slider, x, y);
        }
    }

    private void overlayImage(BufferedImage baseBufferedImage, BufferedImage coverBufferedImage, int x, int y) {
        Graphics2D g2d = baseBufferedImage.createGraphics();
        g2d.drawImage(coverBufferedImage, x, y, coverBufferedImage.getWidth(), coverBufferedImage.getHeight(), (ImageObserver)null);
        g2d.dispose();
    }

    private BufferedImage cutImage(BufferedImage src, BufferedImage template, int xOffset, int yOffset) {
        int width = template.getWidth((ImageObserver)null);
        int height = template.getHeight((ImageObserver)null);
        BufferedImage target = new BufferedImage(width, height, 2);

        for(int y = 0; y < height; ++y) {
            for(int x = 0; x < width; ++x) {
                int rgb = template.getRGB(x, y);
                int alpha = rgb >> 24 & 255;
                if (alpha > 100) {
                    int bgRgb = src.getRGB(xOffset + x, yOffset + y);
                    target.setRGB(x, y, bgRgb);
                }
            }
        }

        return target;
    }

    private BufferedImage deepCopy(BufferedImage src) {
        ColorModel cm = src.getColorModel();
        WritableRaster raster = src.copyData((WritableRaster)null);
        return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), (Hashtable)null);
    }

    private BufferedImage scale(BufferedImage src, int sideLength) {
        BufferedImage target = new BufferedImage(sideLength, sideLength, 2);
        Graphics2D g = target.createGraphics();
        g.drawImage(src, 0, 0, sideLength, sideLength, (ImageObserver)null);
        g.dispose();
        return target;
    }

    private BufferedImage rotate(BufferedImage src, int angle) {
        int sideLength = src.getWidth();
        BufferedImage target = new BufferedImage(sideLength, sideLength, 2);
        Graphics2D g = target.createGraphics();
        g.rotate(Math.toRadians((double)angle), (double)sideLength / 2.0, (double)sideLength / 2.0);
        g.drawImage(src, (BufferedImageOp)null, 0, 0);
        g.dispose();
        return target;
    }

    private BufferedImage readImage(String base64) {
        byte[] bytes = Base64.getDecoder().decode(base64);

        try {
            return ImageIO.read(new ByteArrayInputStream(bytes));
        } catch (IOException var4) {
            throw new SliderCaptchaException(var4);
        }
    }
}

 2.4返回给前端的对象

package com.example.verificationcode.config;

import lombok.Data;

@Data
public class SliderCaptchaRequestResult {
    //图像, base64编码
    private String image;

    //图像高度
    private int imageHeight;

    //图像宽度
    private int imageWidth;

    //滑块图像, base64编码
    private String slider;

    //滑块图像高度
    private int sliderHeight;

    //滑块图像宽度
    private int sliderWidth;

    //滑块y轴坐标
    private int sliderCoordinateY;

    //验证码凭证,前端在登陆时要将token原封不动的返回,用于验证码校验
    private String token;
}

2.5校验接参

package com.example.verificationcode.config;

import lombok.Data;

@Data
public class SliderCaptchaVerifyParam {
    //验证码token
    private String token;

    //验证码值(滑块x轴坐标)
    private Integer value;
}

2.6生成一个滑块验证码对象,验证码校验方法

验证码存入缓存的逻辑可以在这个类中添加

package com.example.verificationcode.utils;

import com.example.verificationcode.config.SliderCaptchaConfig;
import com.example.verificationcode.config.SliderCaptchaRequestResult;
import com.example.verificationcode.config.SliderCaptchaVerifyParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.UUID;

@Component
public class GenerateSliderCaptcha {
    private final SliderCaptchaConfig sliderCaptchaConfig;

    private final SliderCaptchaGenerator sliderCaptchaGenerator;

    private static final Base64.Encoder withoutPaddingUrlBase64Encoder = Base64.getUrlEncoder().withoutPadding();

    public GenerateSliderCaptcha(@Autowired SliderCaptchaConfig SliderCaptchaConfig) {
        this.sliderCaptchaConfig = SliderCaptchaConfig;

        this.sliderCaptchaGenerator = new SliderCaptchaGenerator(86);
        for (var path : SliderCaptchaConfig.getImagePaths()) {
            sliderCaptchaGenerator.addImage(path);
        }
    }


    /**
     * 生成一个验证码
     */
    public SliderCaptchaRequestResult request() {
        //调用工具类方法,创建一个验证码对象
        var sliderCaptcha = sliderCaptchaGenerator.newOne();
        //将验证码对象的属性赋值给SliderCaptchaRequestResult返回给前端
        var result = new SliderCaptchaRequestResult();
        result.setImage(sliderCaptcha.getImageDataUrl());
        result.setImageWidth(sliderCaptcha.getImageWidth());
        result.setImageHeight(sliderCaptcha.getImageHeight());
        result.setSlider(sliderCaptcha.getSliderDataUrl());
        result.setSliderWidth(sliderCaptcha.getSliderWidth());
        result.setSliderHeight(sliderCaptcha.getSliderHeight());
        result.setSliderCoordinateY(sliderCaptcha.getSliderCoordinateY());

        UUID uuid = UUID.randomUUID();
        ByteBuffer buffer = ByteBuffer.wrap(new byte[16]);
        buffer.putLong(uuid.getMostSignificantBits());
        buffer.putLong(uuid.getLeastSignificantBits());
        String token = withoutPaddingUrlBase64Encoder.encodeToString(buffer.array());
        result.setToken(token);

        // TODO 以token为key,sliderCaptcha.getSliderCoordinateX()为值存储到redis中
        // redisTemplate.opsForValue().set(token, sliderCaptcha.getSliderCoordinateX());

        return result;
    }

    /**
     * 在登陆时调用校验验证码
     * @param param
     * @throws RuntimeException
     */
    public void verify(SliderCaptchaVerifyParam param) throws RuntimeException {
        if (sliderCaptchaConfig.isDisabled()) {
            return;
        }
        // TODO 以param.getToken()为key,取出sliderCaptcha.getSliderCoordinateX()和前端传入的value比较
        // var sliderCoordinateX = redisTemplate.opsForValue().get(param.getToken());
        var sliderCoordinateX = Double.valueOf(10d);
        if (sliderCoordinateX == null) {
            throw new RuntimeException("验证码错误");
        }

        // TODO 删除redis中的param.getToken()
        // redisTemplate.remove(param.getToken());

        if (Math.abs(sliderCoordinateX - param.getValue()) > sliderCaptchaConfig.getMatchPrecision()) {
            throw new RuntimeException("验证码错误");
        }
    }

}

滑块验证码是一种常见的人机交互验证方式,主要用于防止恶意攻击和机器人攻击。下面是一个简单的Java实现滑块验证码的示例代码: 1. 首先,需要在前端页面上实现一个滑块组件,并在后台生成一个随机的验证码图片。 2. 然后,用户需要按住滑块并将其拖动到正确的位置,以验证自己是一个真正的人类用户。 3. 在后台,需要验证用户拖动滑块的位置是否正确,以确保用户通过了验证。 下面是一个基于Spring Boot框架的简单示例代码: 1. 在前端页面中添加如下代码: ```html <div class="slider-container"> <div class="slider-background"></div> <div class="slider-handle"></div> </div> ``` 2. 在后台代码中,需要生成一个随机的验证码图片,并将验证码信息保存在Session中,以便后续验证。以下是一个简单的验证码生成器示例代码: ```java import java.awt.*; import java.awt.image.BufferedImage; import java.util.Random; public class CaptchaGenerator { private static final int IMAGE_WIDTH = 200; private static final int IMAGE_HEIGHT = 80; private static final int LINE_COUNT = 20; private static final int CHAR_COUNT = 4; private static final String CHAR_SET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final int CHAR_SPACE = 20; private static final int CHAR_FONT_SIZE = 50; public static BufferedImage generate(String captcha) { BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); g.setColor(Color.BLACK); Random random = new Random(); for (int i = 0; i < LINE_COUNT; i++) { int x1 = random.nextInt(IMAGE_WIDTH); int y1 = random.nextInt(IMAGE_HEIGHT); int x2 = random.nextInt(IMAGE_WIDTH); int y2 = random.nextInt(IMAGE_HEIGHT); g.drawLine(x1, y1, x2, y2); } Font font = new Font("Arial", Font.BOLD, CHAR_FONT_SIZE); g.setFont(font); int x = (IMAGE_WIDTH - CHAR_COUNT * CHAR_FONT_SIZE - (CHAR_COUNT - 1) * CHAR_SPACE) / 2; int y = (IMAGE_HEIGHT - CHAR_FONT_SIZE) / 2 + CHAR_FONT_SIZE; for (int i = 0; i < captcha.length(); i++) { char c = captcha.charAt(i); g.drawString(String.valueOf(c), x, y); x += CHAR_FONT_SIZE + CHAR_SPACE; } g.dispose(); return image; } public static String generateCaptcha() { Random random = new Random(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < CHAR_COUNT; i++) { char c = CHAR_SET.charAt(random.nextInt(CHAR_SET.length())); sb.append(c); } return sb.toString(); } } ``` 3. 在Controller中,需要处理滑块验证请求,并进行验证码验证。以下是一个简单的Controller示例代码: ```java import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.awt.image.BufferedImage; import java.io.IOException; @Controller public class CaptchaController { @GetMapping("/captcha") public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException { String captcha = CaptchaGenerator.generateCaptcha(); BufferedImage image = CaptchaGenerator.generate(captcha); HttpSession session = request.getSession(); session.setAttribute("captcha", captcha); response.setContentType("image/png"); response.getOutputStream().write(ImageUtil.toByteArray(image)); } @PostMapping("/captcha/verify") @ResponseBody public boolean verify(@RequestParam String captcha, HttpSession session) { String expectedCaptcha = (String) session.getAttribute("captcha"); return captcha.equals(expectedCaptcha); } } ``` 以上是一个简单的Java实现滑块验证码的示例代码。为了实现更好的安全性,实际应用中需要进一步加强验证机制,例如添加时间限制、IP限制等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值