欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot圖片驗證碼功能模塊

 更新時間:2022年04月15日 09:44:33   作者:興趣使然的小小  
用戶登錄幾乎是一個線上系統(tǒng)必不可少且使用相對比較頻繁的一個模塊,為了防止惡意暴力嘗試,防止洪水攻擊、防止腳本自動提交等,驗證碼是一個較為便捷且行之有效的預(yù)防手段,這篇文章主要介紹了springboot圖片驗證碼功能模塊,需要的朋友可以參考下

前言:

大家好!我是小?。〗裉煳覀冇梦宸昼妬碛胹pringboot實現(xiàn)我們常用的圖形驗證碼功能模塊!

用戶登錄幾乎是一個線上系統(tǒng)必不可少且使用相對比較頻繁的一個模塊,為了防止惡意暴力嘗試,防止洪水攻擊、防止腳本自動提交等,驗證碼是一個較為便捷且行之有效的預(yù)防手段。

具體效果如下:

請?zhí)砑訄D片描述

第一步:工具類

該工具類為生成驗證碼圖片的核心,直接拷貝到項目即可,無需做修改;可個性化的參數(shù)全部對外提供的API,比如 字體大小,背景顏色,干擾線數(shù)量,高寬等都可以根據(jù)自己的需求設(shè)置對應(yīng)參數(shù);

代碼幾乎每一行都加了詳細(xì)的注釋;如果遇上特殊的個性化需求,調(diào)整一下這個工具類即可實現(xiàn)。

package com.feng.util;
/**
 * @return null
 * @author Ladidol
 * @description
 * @date 2022/4/11 22:15
 */
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
 * 圖形驗證碼生成
 */
public class VerifyUtil {
    // 默認(rèn)驗證碼字符集
    private static final char[] chars = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    // 默認(rèn)字符數(shù)量
    private final Integer SIZE;
    // 默認(rèn)干擾線數(shù)量
    private final int LINES;
    // 默認(rèn)寬度
    private final int WIDTH;
    // 默認(rèn)高度
    private final int HEIGHT;
    // 默認(rèn)字體大小
    private final int FONT_SIZE;
    // 默認(rèn)字體傾斜
    private final boolean TILT;
    private final Color BACKGROUND_COLOR;
    /**
     * 初始化基礎(chǔ)參數(shù)
     *
     * @param builder
     */
    private VerifyUtil(Builder builder) {
        SIZE = builder.size;
        LINES = builder.lines;
        WIDTH = builder.width;
        HEIGHT = builder.height;
        FONT_SIZE = builder.fontSize;
        TILT = builder.tilt;
        BACKGROUND_COLOR = builder.backgroundColor;
    }
    /**
     * 實例化構(gòu)造器對象
     *
     * @return
     */
    public static Builder newBuilder() {
        return new Builder();
    }
    /**
     * @return 生成隨機(jī)驗證碼及圖片
     * Object[0]:驗證碼字符串;
     * Object[1]:驗證碼圖片。
     */
    public Object[] createImage() {
        StringBuffer sb = new StringBuffer();
        // 創(chuàng)建空白圖片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        // 獲取圖片畫筆
        Graphics2D graphic = image.createGraphics();
        // 設(shè)置抗鋸齒
        graphic.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 設(shè)置畫筆顏色
        graphic.setColor(BACKGROUND_COLOR);
        // 繪制矩形背景
        graphic.fillRect(0, 0, WIDTH, HEIGHT);
        // 畫隨機(jī)字符
        Random ran = new Random();
        //graphic.setBackground(Color.WHITE);
        // 計算每個字符占的寬度,這里預(yù)留一個字符的位置用于左右邊距
        int codeWidth = WIDTH / (SIZE + 1);
        // 字符所處的y軸的坐標(biāo)
        int y = HEIGHT * 3 / 4;
        for (int i = 0; i < SIZE; i++) {
            // 設(shè)置隨機(jī)顏色
            graphic.setColor(getRandomColor());
            // 初始化字體
            Font font = new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE);
            if (TILT) {
                // 隨機(jī)一個傾斜的角度 -45到45度之間
                int theta = ran.nextInt(45);
                // 隨機(jī)一個傾斜方向 左或者右
                theta = (ran.nextBoolean() == true) ? theta : -theta;
                AffineTransform affineTransform = new AffineTransform();
                affineTransform.rotate(Math.toRadians(theta), 0, 0);
                font = font.deriveFont(affineTransform);
            }
            // 設(shè)置字體大小
            graphic.setFont(font);
            // 計算當(dāng)前字符繪制的X軸坐標(biāo)
            int x = (i * codeWidth) + (codeWidth / 2);
            // 取隨機(jī)字符索引
            int n = ran.nextInt(chars.length);
            // 得到字符文本
            String code = String.valueOf(chars[n]);
            // 畫字符
            graphic.drawString(code, x, y);
            // 記錄字符
            sb.append(code);
        }
        // 畫干擾線
        for (int i = 0; i < LINES; i++) {
            // 設(shè)置隨機(jī)顏色
            graphic.setColor(getRandomColor());
            // 隨機(jī)畫線
            graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT), ran.nextInt(WIDTH), ran.nextInt(HEIGHT));
        }
        // 返回驗證碼和圖片
        return new Object[]{sb.toString(), image};
    }
    /**
     * 隨機(jī)取色
     */
    private Color getRandomColor() {
        Random ran = new Random();
        Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
        return color;
    }
    /**
     * 構(gòu)造器對象
     */
    public static class Builder {
        // 默認(rèn)字符數(shù)量
        private int size = 4;
        // 默認(rèn)干擾線數(shù)量
        private int lines = 10;
        // 默認(rèn)寬度
        private int width = 80;
        // 默認(rèn)高度
        private int height = 35;
        // 默認(rèn)字體大小
        private int fontSize = 25;
        // 默認(rèn)字體傾斜
        private boolean tilt = true;
        //背景顏色
        private Color backgroundColor = Color.LIGHT_GRAY;
        public Builder setSize(int size) {
            this.size = size;
            return this;
        }
        public Builder setLines(int lines) {
            this.lines = lines;
            return this;
        }
        public Builder setWidth(int width) {
            this.width = width;
            return this;
        }
        public Builder setHeight(int height) {
            this.height = height;
            return this;
        }
        public Builder setFontSize(int fontSize) {
            this.fontSize = fontSize;
            return this;
        }
        public Builder setTilt(boolean tilt) {
            this.tilt = tilt;
            return this;
        }
        public Builder setBackgroundColor(Color backgroundColor) {
            this.backgroundColor = backgroundColor;
            return this;
        }
        public VerifyUtil build() {
            return new VerifyUtil(this);
        }
    }
}

第二步:圖片生成:

使用默認(rèn)參數(shù):

//生成圖片驗證碼
Object[] verify = VerifyUtil.newBuilder().build().createImage();

自定義參數(shù)生成:

// 這個根據(jù)自己的需要設(shè)置對應(yīng)的參數(shù)來實現(xiàn)個性化
// 返回的數(shù)組第一個參數(shù)是生成的驗證碼,第二個參數(shù)是生成的圖片
Object[] objs = VerifyUtil.newBuilder()
        .setWidth(120)   //設(shè)置圖片的寬度
        .setHeight(35)   //設(shè)置圖片的高度
        .setSize(6)      //設(shè)置字符的個數(shù)
        .setLines(10)    //設(shè)置干擾線的條數(shù)
        .setFontSize(25) //設(shè)置字體的大小
        .setTilt(true)   //設(shè)置是否需要傾斜
        .setBackgroundColor(Color.WHITE) //設(shè)置驗證碼的背景顏色
        .build()         //構(gòu)建VerifyUtil項目
        .createImage();  //生成圖片

整合到springboot項目中:

需要引入的maven依賴:

        <!--redis相關(guān)配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- redis 連接池 -->
        <!--新版本連接池lettuce-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- 圖形驗證碼 -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.10</version>
        </dependency>

獲取相關(guān)的驗證碼:

service層:

package com.feng.service;

import org.cuit.epoch.result.Result;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @return null
 * @author Ladidol
 * @description
 * @date 2022/4/11 22:15
 */

public interface VerifyService {
    /**
     * 創(chuàng)建圖片驗證碼
     * @param response
     * @param request
     * @throws IOException
     */
    void createCode(HttpServletResponse response, HttpServletRequest request) throws IOException;
    /**
     * 檢查圖片驗證碼
     * @param
     * @param
     * @throws IOException
     */
    Result<String> checkCode(String verificationCode);
}

serviceimpl層:

package com.feng.service.impl;
import com.feng.service.VerifyService;
import com.feng.util.RedisServiceImpl;
import com.google.common.net.HttpHeaders;
import com.feng.util.VerifyUtil;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.time.Duration;
/**
 * @return null
 * @author Ladidol
 * @description
 * @date 2022/4/11 22:15
 */
@Service
public class VerifyServiceImpl implements VerifyService {
    @Resource
    RedisServiceImpl redisUtil;
    /**
     * 生成圖片驗證碼
     * @param response
     * @param request
     * @throws IOException
     */
    @Override
    public void createCode(HttpServletResponse response, HttpServletRequest request) throws IOException {
        //獲取session
        HttpSession session = request.getSession();
        //獲得sessionId
        String id = session.getId();
        System.out.println();
        ResponseCookie cookie = ResponseCookie.from("JSESSIONID",id)
                .secure(true)
                .domain("")
                .path("/")
                .maxAge(Duration.ofHours(1))
                .sameSite("None")
                .build();
        //清除之前緩存的圖片驗證碼
        if (!String.valueOf(request.getSession().getAttribute("SESSION_VERIFY_CODE_"+id)).isEmpty()){
            String getVerify = String.valueOf(request.getSession().getAttribute("SESSION_VERIFY_CODE_"+id));
            redisUtil.del(getVerify);
            System.out.println("清除成功");
        }
        //生成圖片驗證碼,用的默認(rèn)參數(shù)
        Object[] verify = VerifyUtil.newBuilder().build().createImage();
        //將驗證碼存入session
        session.setAttribute("SESSION_VERIFY_CODE_" + id, verify[0]);
        //打印驗證碼
        System.out.println(verify[0]);
        //將驗證碼存入redis
        redisUtil.set((String) verify[0],id,5*60);
        //將圖片傳給瀏覽器
        BufferedImage image = (BufferedImage) verify[1];
        response.setContentType("image/png");
        response.setHeader(HttpHeaders.SET_COOKIE,cookie.toString());
        OutputStream ops = response.getOutputStream();
        ImageIO.write(image,"png",ops);
    }
    @Override
    public Result<String> checkCode(String verificationCode){
        if (!redisUtil.hasKey(verificationCode)){
            return new Result<>(false,"驗證碼錯誤");
        }
        redisUtil.del(verificationCode);
        return R.success();
    }
}

這里面還會用到redis相關(guān)的工具類,我就不列出來了,想要的話可以看我以前的文章工具類戳這里

controller層:
這里有用到@RequiredArgsConstructor, 就是簡單的注入而已, 如果想要詳細(xì)了解戳這里

package com.feng.controller;
import lombok.RequiredArgsConstructor;
import com.feng.annotation.LimitRequest;
import com.feng.service.VerifyService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @return null
 * @author Ladidol
 * @description 這里主要就是多種驗證碼和登錄相關(guān)的東西
 * @date 2022/4/11 21:46
 */
@RestController
@RequestMapping("/verify")
@RequiredArgsConstructor//這是在lombok工具給的注入方式,真帥
public class VerifyController {
    private final VerifyService verifyService;
    /**
     * 獲取圖片驗證碼
     */
    @LimitRequest(count = 5)//這個注解就是表示, 你在限制時間里(我們這里默認(rèn)是六秒鐘), 只能請求五次
    @GetMapping("/getCode")
    public void getCode(HttpServletResponse response, HttpServletRequest request) throws IOException {
        verifyService.createCode(response, request);
    }
    @LimitRequest(count = 5)//這個注解就是表示, 你在限制時間里(我們這里默認(rèn)是六秒鐘), 只能請求五次
    @GetMapping("/checkCode")
    public Result<String> checkCode(String code){
        return verifyService.checkCode(code);
    }
}

這里為了不被一直無限制的訪問該服務(wù), 我們用了一個限制ip訪問次數(shù)的注解@LimitRequest

annotion包下的注解類:

package com.feng.annotation;

import java.lang.annotation.*;

/**
 * @return null
 * @author Ladidol
 * @description 限制ip訪問次數(shù)注解
 * @date 2022/4/11 22:15
 */

@Documented
@Target(ElementType.METHOD) // 說明該注解只能放在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRequest {
    long time() default 6000; // 限制時間 單位:毫秒
    int count() default 3; // 允許請求的次數(shù)

}

aspect包下的切面類:

package com.feng.aspect;

import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import com.feng.annotation.LimitRequest;
import org.cuit.epoch.exception.AppException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @return null
 * @author Ladidol
 * @description
 * @date 2022/4/11 22:15
 */

@Aspect
@Component
public class LimitRequestAspect {

    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();

    // 定義切點
    // 讓所有有@LimitRequest注解的方法都執(zhí)行切面方法
    @Pointcut("@annotation(limitRequest)")
    public void excudeService(LimitRequest limitRequest) {
    }

    @Around("excudeService(limitRequest)")
    public Object doAround(ProceedingJoinPoint pjp, LimitRequest limitRequest) throws Throwable {

        // 獲得request對象
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        // 獲取Map對象, 如果沒有則返回默認(rèn)值
        // 第一個參數(shù)是key, 第二個參數(shù)是默認(rèn)值
        ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
        Integer uCount = uc.getOrDefault(request.getRemoteAddr(), 0);


        if (uCount >= limitRequest.count()) { // 超過次數(shù),不執(zhí)行目標(biāo)方法
            System.out.println("接口請求超過次數(shù)!");
            throw new AppException("接口請求超過次數(shù)!");
        } else if (uCount == 0) { // 第一次請求時,設(shè)置有效時間
//
            uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, limitRequest.time(), TimeUnit.MILLISECONDS);
        } else { // 未超過次數(shù), 記錄加一
            uc.put(request.getRemoteAddr(), uCount + 1);
        }
        book.put(request.getRequestURI(), uc);

        // result的值就是被攔截方法的返回值
        Object result = pjp.proceed();

        return result;
    }
}

為了捕獲全局的異常拋出, 且符合restful規(guī)范我們加一個這個處理類:

handle包下面的全局異常類:

package org.cuit.epoch.handler;
import lombok.extern.log4j.Log4j2;
import org.cuit.epoch.exception.AppException;
import org.cuit.epoch.result.R;
import org.cuit.epoch.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e) {
        log.error(e.getMessage());
        e.printStackTrace();
        return R.fail(e.getMessage());
    }
    @ExceptionHandler(AppException.class)
    @ResponseBody
    public Result error(AppException e) {
        log.error(e.getMessage());
        e.printStackTrace();
        return R.fail(e.getMessage());
    }
}

application.yaml文件:

spring:
  cache:
    type:
      redis
  redis: #redis連接配置
    host: 自己redis的ip地址
    port: redis端口
    password: 密碼
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 0ms


最終項目結(jié)構(gòu)如下:

image-20220412172908356

先得到一個驗證碼:

image-20220411225740692

驗證一下是否成功:

成功結(jié)果:

image-20220411225731282

驗證失敗結(jié)果:

image-20220412173201994

當(dāng)請求在規(guī)定時間內(nèi)的請求數(shù)超過規(guī)定的數(shù)量時或有報錯:

image-20220412173025475

參考:

連接1

到此這篇關(guān)于springboot圖片驗證碼的文章就介紹到這了,更多相關(guān)springboot圖片驗證碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用Tess4J實現(xiàn)圖像識別方式

    Java使用Tess4J實現(xiàn)圖像識別方式

    這篇文章主要介紹了Java使用Tess4J實現(xiàn)圖像識別方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java發(fā)起http請求的完整步驟記錄

    Java發(fā)起http請求的完整步驟記錄

    這篇文章主要給大家介紹了關(guān)于Java發(fā)起http請求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • IntellJ IDEA神器使用技巧(小結(jié))

    IntellJ IDEA神器使用技巧(小結(jié))

    這篇文章主要介紹了IntellJ IDEA神器使用技巧(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Spring實現(xiàn)擁有者權(quán)限驗證的方法示例

    Spring實現(xiàn)擁有者權(quán)限驗證的方法示例

    這篇文章主要介紹了Spring實現(xiàn)擁有者權(quán)限驗證的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • java 在圖片上寫字,兩個圖片合并的實現(xiàn)方法

    java 在圖片上寫字,兩個圖片合并的實現(xiàn)方法

    下面小編就為大家?guī)硪黄猨ava 在圖片上寫字,兩個圖片合并的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • JAVASE系統(tǒng)實現(xiàn)抽卡功能

    JAVASE系統(tǒng)實現(xiàn)抽卡功能

    這篇文章主要為大家詳細(xì)介紹了JAVASE系統(tǒng)實現(xiàn)抽卡功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • 關(guān)于Jackson的JSON工具類封裝 JsonUtils用法

    關(guān)于Jackson的JSON工具類封裝 JsonUtils用法

    這篇文章主要介紹了關(guān)于Jackson的JSON工具類封裝 JsonUtils用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringMVC中ModelAndView的使用及說明

    SpringMVC中ModelAndView的使用及說明

    這篇文章主要介紹了SpringMVC中ModelAndView的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 深入理解Java 線程通信

    深入理解Java 線程通信

    這篇文章主要介紹了Java 線程通信的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 解決springboot引入swagger2不生效問題

    解決springboot引入swagger2不生效問題

    這篇文章主要為大家介紹了解決springboot引入swagger2不生效問題的方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05

最新評論