亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring框架實現(xiàn)滑動驗證碼功能的代碼示例

 更新時間:2024年07月28日 16:04:47   作者:天河歸來  
之前項目需要在驗證碼模塊,增加滑動驗證碼,用來給手機端使用的,大概看了下,主要方法就是將圖片切割,然后記住偏移量,進行滑動,所以本文給大家介紹了Spring框架實現(xiàn)滑動驗證碼功能的方法示例,需要的朋友可以參考下

1. 整體描述

之前項目需要在驗證碼模塊,增加滑動驗證碼,用來給手機端使用的,大概看了下,主要方法就是將圖片切割,然后記住偏移量,進行滑動,前端驗證的時候,需要用前端傳入的偏移量和生成的偏移量進行對比,如果在閾值之內(nèi),就驗證通過,否則就不通過。具體實現(xiàn)方式見下文。之前沒時間寫,最近記錄一下。

2. 具體實現(xiàn)

本工程主要依賴springboot框架,并且需要redis存驗證碼的信息,還需要幾個圖片,用來生成驗證碼。

2.1 滑動驗證碼實體類

package com.thcb.captchademo.captcha.domain;

import lombok.Data;


/**
 * 滑動驗證碼
 *
 * @author thcb
 * @date 2023-05-25
 */

@Data
public class Captcha {

    /**
     * 隨機字符串
     **/
    private String nonceStr;
    /**
     * 驗證值
     **/
    private String value;
    /**
     * 生成的畫布的base64
     **/
    private String canvasSrc;
    /**
     * 畫布寬度
     **/
    private Integer canvasWidth;
    /**
     * 畫布高度
     **/
    private Integer canvasHeight;
    /**
     * 生成的阻塞塊的base64
     **/
    private String blockSrc;
    /**
     * 阻塞塊寬度
     **/
    private Integer blockWidth;
    /**
     * 阻塞塊高度
     **/
    private Integer blockHeight;
    /**
     * 阻塞塊凸凹半徑
     **/
    private Integer blockRadius;
    /**
     * 阻塞塊的橫軸坐標
     **/
    private Integer blockX;
    /**
     * 阻塞塊的縱軸坐標
     **/
    private Integer blockY;
    /**
     * 圖片獲取位置
     **/
    private Integer place;
}

2.2 滑動驗證碼登錄VO

package com.thcb.captchademo.captcha.domain;

import lombok.Data;

/**
 * 滑動驗證碼登錄Vo
 *
 * @author thcb
 * @date 2023-05-25
 */

@Data
public class LoginVo {
    /**
     * 隨機字符串
     **/
    private String nonceStr;
    /**
     * 驗證值
     **/
    private String value;
}

2.3 滑動驗證碼接口返回類

package com.thcb.captchademo.captcha.utils;

import java.util.HashMap;

/**
 * 操作消息提醒
 *
 * @author thcb
 */
public class AjaxResult extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    /**
     * 狀態(tài)碼
     */
    public static final String CODE_TAG = "code";

    /**
     * 返回內(nèi)容
     */
    public static final String MSG_TAG = "msg";

    /**
     * 數(shù)據(jù)對象
     */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一個新創(chuàng)建的 AjaxResult 對象,使其表示一個空消息。
     */
    public AjaxResult() {
    }

    /**
     * 初始化一個新創(chuàng)建的 AjaxResult 對象
     *
     * @param code 狀態(tài)碼
     * @param msg  返回內(nèi)容
     */
    public AjaxResult(int code, String msg) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一個新創(chuàng)建的 AjaxResult 對象
     *
     * @param code 狀態(tài)碼
     * @param msg  返回內(nèi)容
     * @param data 數(shù)據(jù)對象
     */
    public AjaxResult(int code, String msg, Object data) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (data != null && !data.equals("")) {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success() {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功數(shù)據(jù)
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data) {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回內(nèi)容
     * @return 成功消息
     */
    public static AjaxResult success(String msg) {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg  返回內(nèi)容
     * @param data 數(shù)據(jù)對象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data) {
        return new AjaxResult(200, msg, data);
    }

    /**
     * 返回錯誤消息
     *
     * @return
     */
    public static AjaxResult error() {
        return AjaxResult.error("操作失敗");
    }

    /**
     * 返回錯誤消息
     *
     * @param msg 返回內(nèi)容
     * @return 警告消息
     */
    public static AjaxResult error(String msg) {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回錯誤消息
     *
     * @param msg  返回內(nèi)容
     * @param data 數(shù)據(jù)對象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data) {
        return new AjaxResult(500, msg, data);
    }

    /**
     * 返回錯誤消息
     *
     * @param code 狀態(tài)碼
     * @param msg  返回內(nèi)容
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg) {
        return new AjaxResult(code, msg, null);
    }
}

2.4 滑動驗證碼工具類

此類是核心類,主要實現(xiàn)了圖片的切割功能。

package com.thcb.captchademo.captcha.utils;

import com.thcb.captchademo.captcha.domain.Captcha;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.Objects;
import java.util.Random;

/**
 * 滑動驗證碼工具類
 *
 * @author thcb
 * @date 2023-05-25
 */
public class CaptchaUtils {

    /**
     * 網(wǎng)絡(luò)圖片地址
     **/
    private final static String IMG_URL = "https://loyer.wang/view/ftp/wallpaper/%s.jpg";

    /**
     * 本地圖片地址
     **/
    private final static String IMG_PATH = "E:\\caphcha\\%s.jpg";

    /**
     * 入?yún)⑿r炘O(shè)置默認值
     **/
    public static void checkCaptcha(Captcha captcha) {
        //設(shè)置畫布寬度默認值
        if (captcha.getCanvasWidth() == null) {
            captcha.setCanvasWidth(320);
        }
        //設(shè)置畫布高度默認值
        if (captcha.getCanvasHeight() == null) {
            captcha.setCanvasHeight(155);
        }
        //設(shè)置阻塞塊寬度默認值
        if (captcha.getBlockWidth() == null) {
            captcha.setBlockWidth(65);
        }
        //設(shè)置阻塞塊高度默認值
        if (captcha.getBlockHeight() == null) {
            captcha.setBlockHeight(55);
        }
        //設(shè)置阻塞塊凹凸半徑默認值
        if (captcha.getBlockRadius() == null) {
            captcha.setBlockRadius(9);
        }
        //設(shè)置圖片來源默認值
        if (captcha.getPlace() == null) {
            captcha.setPlace(1);
        }
    }

    /**
     * 獲取指定范圍內(nèi)的隨機數(shù)
     **/
    public static int getNonceByRange(int start, int end) {
        Random random = new Random();
        return random.nextInt(end - start + 1) + start;
    }

    /**
     * 獲取驗證碼資源圖
     **/
    public static BufferedImage getBufferedImage(Integer place) {
        try {
            //隨機圖片

            //獲取網(wǎng)絡(luò)資源圖片
            if (0 == place) {
                int nonce = getNonceByRange(0, 1000);
                String imgUrl = String.format(IMG_URL, nonce);
                URL url = new URL(imgUrl);
                return ImageIO.read(url.openStream());
            }
            //獲取本地圖片
            else {
                int nonce = getNonceByRange(0, 20);
                String imgPath = String.format(IMG_PATH, nonce);
                File file = new File(imgPath);
                return ImageIO.read(file);
            }
        } catch (Exception e) {
            System.out.println("獲取拼圖資源失敗");
            //異常處理
            return null;
        }
    }

    /**
     * 調(diào)整圖片大小
     **/
    public static BufferedImage imageResize(BufferedImage bufferedImage, int width, int height) {
        Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics2D = resultImage.createGraphics();
        graphics2D.drawImage(image, 0, 0, null);
        graphics2D.dispose();
        return resultImage;
    }

    /**
     * 摳圖,并生成阻塞塊
     **/
    public static void cutByTemplate(BufferedImage canvasImage, BufferedImage blockImage, int blockWidth, int blockHeight, int blockRadius, int blockX, int blockY) {
        BufferedImage waterImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);
        //阻塞塊的輪廓圖
        int[][] blockData = getBlockData(blockWidth, blockHeight, blockRadius);
        //創(chuàng)建阻塞塊具體形狀
        for (int i = 0; i < blockWidth; i++) {
            for (int j = 0; j < blockHeight; j++) {
                try {
                    //原圖中對應(yīng)位置變色處理
                    if (blockData[i][j] == 1) {
                        //背景設(shè)置為黑色
                        waterImage.setRGB(i, j, Color.BLACK.getRGB());
                        blockImage.setRGB(i, j, canvasImage.getRGB(blockX + i, blockY + j));
                        //輪廓設(shè)置為白色,取帶像素和無像素的界點,判斷該點是不是臨界輪廓點
                        if (blockData[i + 1][j] == 0 || blockData[i][j + 1] == 0 || blockData[i - 1][j] == 0 || blockData[i][j - 1] == 0) {
                            blockImage.setRGB(i, j, Color.WHITE.getRGB());
                            waterImage.setRGB(i, j, Color.WHITE.getRGB());
                        }
                    }
                    //這里把背景設(shè)為透明
                    else {
                        blockImage.setRGB(i, j, Color.TRANSLUCENT);
                        waterImage.setRGB(i, j, Color.TRANSLUCENT);
                    }
                } catch (ArrayIndexOutOfBoundsException e) {
                    //防止數(shù)組下標越界異常
                }
            }
        }
        //在畫布上添加阻塞塊水印
        addBlockWatermark(canvasImage, waterImage, blockX, blockY);
    }

    /**
     * 構(gòu)建拼圖輪廓軌跡
     **/
    private static int[][] getBlockData(int blockWidth, int blockHeight, int blockRadius) {
        int[][] data = new int[blockWidth][blockHeight];
        double po = Math.pow(blockRadius, 2);
        //隨機生成兩個圓的坐標,在4個方向上 隨機找到2個方向添加凸/凹
        //凸/凹1
        Random random1 = new Random();
        int face1 = random1.nextInt(4);
        //凸/凹2
        int face2;
        //保證兩個凸/凹不在同一位置
        do {
            Random random2 = new Random();
            face2 = random2.nextInt(4);
        } while (face1 == face2);
        //獲取凸/凹起位置坐標
        int[] circle1 = getCircleCoords(face1, blockWidth, blockHeight, blockRadius);
        int[] circle2 = getCircleCoords(face2, blockWidth, blockHeight, blockRadius);
        //隨機凸/凹類型
        int shape = getNonceByRange(0, 1);
        //圓的標準方程 (x-a)2+(y-b)2=r2,標識圓心(a,b),半徑為r的圓
        //計算需要的小圖輪廓,用二維數(shù)組來表示,二維數(shù)組有兩張值,0和1,其中0表示沒有顏色,1有顏色
        for (int i = 0; i < blockWidth; i++) {
            for (int j = 0; j < blockHeight; j++) {
                data[i][j] = 0;
                //創(chuàng)建中間的方形區(qū)域
                if ((i >= blockRadius && i <= blockWidth - blockRadius && j >= blockRadius && j <= blockHeight - blockRadius)) {
                    data[i][j] = 1;
                }
                double d1 = Math.pow(i - Objects.requireNonNull(circle1)[0], 2) + Math.pow(j - circle1[1], 2);
                double d2 = Math.pow(i - Objects.requireNonNull(circle2)[0], 2) + Math.pow(j - circle2[1], 2);
                //創(chuàng)建兩個凸/凹
                if (d1 <= po || d2 <= po) {
                    data[i][j] = shape;
                }
            }
        }
        return data;
    }

    /**
     * 根據(jù)朝向獲取圓心坐標
     */
    private static int[] getCircleCoords(int face, int blockWidth, int blockHeight, int blockRadius) {
        //上
        if (0 == face) {
            return new int[]{blockWidth / 2 - 1, blockRadius};
        }
        //左
        else if (1 == face) {
            return new int[]{blockRadius, blockHeight / 2 - 1};
        }
        //下
        else if (2 == face) {
            return new int[]{blockWidth / 2 - 1, blockHeight - blockRadius - 1};
        }
        //右
        else if (3 == face) {
            return new int[]{blockWidth - blockRadius - 1, blockHeight / 2 - 1};
        }
        return null;
    }

    /**
     * 在畫布上添加阻塞塊水印
     */
    private static void addBlockWatermark(BufferedImage canvasImage, BufferedImage blockImage, int x, int y) {
        Graphics2D graphics2D = canvasImage.createGraphics();
        graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.8f));
        graphics2D.drawImage(blockImage, x, y, null);
        graphics2D.dispose();
    }

    /**
     * BufferedImage轉(zhuǎn)BASE64
     */
    public static String toBase64(BufferedImage bufferedImage, String type) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, type, byteArrayOutputStream);
            String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
            return String.format("data:image/%s;base64,%s", type, base64);
        } catch (IOException e) {
            System.out.println("圖片資源轉(zhuǎn)換BASE64失敗");
            //異常處理
            return null;
        }
    }
}

2.5 滑動驗證碼Service

service層,封裝了工具類的一些方法,這里為了簡單沒寫接口,正常應(yīng)該是service和impl兩個類。

package com.thcb.captchademo.captcha.service;

import com.thcb.captchademo.captcha.domain.Captcha;
import com.thcb.captchademo.captcha.utils.CaptchaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.awt.image.BufferedImage;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 滑動驗證碼Service
 *
 * @author thcb
 * @date 2023-05-25
 */

@Service
public class CaptchaService {
    /**
     * 拼圖驗證碼允許偏差
     **/
    private static Integer ALLOW_DEVIATION = 3;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 校驗驗證碼
     *
     * @param imageKey
     * @param imageCode
     * @return boolean
     **/
    public String checkImageCode(String imageKey, String imageCode) {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String key = "imageCode:" + imageKey;
        String text = ops.get(key);
        if (text == null || text.equals("")) {
            return "驗證碼已失效";
        }
        // 根據(jù)移動距離判斷驗證是否成功
        if (Math.abs(Integer.parseInt(text) - Integer.parseInt(imageCode)) > ALLOW_DEVIATION) {
            return "驗證失敗,請控制拼圖對齊缺口";
        }
        // 驗證成功,刪除redis緩存
        stringRedisTemplate.delete(key);
        return null;
    }

    /**
     * 緩存驗證碼,有效期1分鐘
     *
     * @param key
     * @param code
     **/
    public void saveImageCode(String key, String code) {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        ops.set("imageCode:" + key, code, 60, TimeUnit.SECONDS);
    }

    /**
     * 獲取驗證碼拼圖(生成的摳圖和帶摳圖陰影的大圖及摳圖坐標)
     **/
    public Object getCaptcha(Captcha captcha) {
        //參數(shù)校驗
        CaptchaUtils.checkCaptcha(captcha);
        //獲取畫布的寬高
        int canvasWidth = captcha.getCanvasWidth();
        int canvasHeight = captcha.getCanvasHeight();
        //獲取阻塞塊的寬高/半徑
        int blockWidth = captcha.getBlockWidth();
        int blockHeight = captcha.getBlockHeight();
        int blockRadius = captcha.getBlockRadius();
        //獲取資源圖
        BufferedImage canvasImage = CaptchaUtils.getBufferedImage(captcha.getPlace());
        //調(diào)整原圖到指定大小
        canvasImage = CaptchaUtils.imageResize(canvasImage, canvasWidth, canvasHeight);
        //隨機生成阻塞塊坐標
        int blockX = CaptchaUtils.getNonceByRange(blockWidth, canvasWidth - blockWidth - 10);
        int blockY = CaptchaUtils.getNonceByRange(10, canvasHeight - blockHeight + 1);
        //阻塞塊
        BufferedImage blockImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);
        //新建的圖像根據(jù)輪廓圖顏色賦值,源圖生成遮罩
        CaptchaUtils.cutByTemplate(canvasImage, blockImage, blockWidth, blockHeight, blockRadius, blockX, blockY);
        // 移動橫坐標
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
        // 緩存
        saveImageCode(nonceStr, String.valueOf(blockX));
        //設(shè)置返回參數(shù)
        captcha.setNonceStr(nonceStr);
        captcha.setBlockY(blockY);
        captcha.setBlockSrc(CaptchaUtils.toBase64(blockImage, "png"));
        captcha.setCanvasSrc(CaptchaUtils.toBase64(canvasImage, "png"));
        return captcha;
    }
}

2.6 滑動驗證碼Controller

controller層有兩個方法,一個是獲取驗證碼的方法,一個是驗證碼校驗的方法。

package com.thcb.captchademo.captcha.controller;

import com.thcb.captchademo.captcha.domain.Captcha;
import com.thcb.captchademo.captcha.domain.LoginVo;
import com.thcb.captchademo.captcha.service.CaptchaService;
import com.thcb.captchademo.captcha.utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 滑動驗證碼Controller
 *
 * @author thcb
 * @date 2023-05-25
 */

@RestController
@RequestMapping("/captcha")
public class CaptchaController {

    @Autowired
    private CaptchaService captchaService;

    @PostMapping("/slideCaptchaImage")
    public AjaxResult getCaptcha() {
        return AjaxResult.success(captchaService.getCaptcha(new Captcha()));
    }

    @PostMapping(value = "/login")
    public AjaxResult login(@RequestBody LoginVo loginVo) {
        // 驗證碼校驗
        String msg = captchaService.checkImageCode(loginVo.getNonceStr(), loginVo.getValue());
        if (msg != null && !msg.equals("")) {
            return AjaxResult.error(msg);
        }
        return AjaxResult.success();
    }

}

3 總結(jié)

滑動驗證碼功能不算復(fù)雜,可以和項目當(dāng)前已有的驗證碼共存,調(diào)用不同的接口,返回不同類型的驗證碼,當(dāng)然這個就根據(jù)項目具體情況確定了。

以上就是Spring框架實現(xiàn)滑動驗證碼功能的代碼示例的詳細內(nèi)容,更多關(guān)于Spring滑動驗證碼功能的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java之Class.forName()用法案例詳解

    Java之Class.forName()用法案例詳解

    這篇文章主要介紹了Java之Class.forName()用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • LeetCode -- Path Sum III分析及實現(xiàn)方法

    LeetCode -- Path Sum III分析及實現(xiàn)方法

    這篇文章主要介紹了LeetCode -- Path Sum III分析及實現(xiàn)方法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • Java的invoke方法的具體使用

    Java的invoke方法的具體使用

    本文主要介紹了Java的invoke方法的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 如何避免在Java項目里大批量使用if-else?

    如何避免在Java項目里大批量使用if-else?

    想起剛開始接觸JAVA時,若遇到大量流程判斷語句,幾乎滿屏都是if-else語句,多得讓自己都忘了哪里是頭,哪里是尾,但是,縱然滿屏是if-else,但彼時也沒有覺得多別扭.等到編程能力漸漸提升之后,再回過頭去看曾經(jīng)寫過的滿屏if-else時,感覺全都是翔.....,需要的朋友可以參考下
    2021-06-06
  • Java集合和IO流實現(xiàn)水果攤項目

    Java集合和IO流實現(xiàn)水果攤項目

    最近閑來無事,使用java基礎(chǔ)知識集合和IO流做了一個簡單的小項目,水果攤項目,用到GUI和Mysql數(shù)據(jù)庫搭建,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2021-06-06
  • Springboot3+Redis實現(xiàn)消息隊列的多種方法小結(jié)

    Springboot3+Redis實現(xiàn)消息隊列的多種方法小結(jié)

    本文主要介紹了Springboot3+Redis實現(xiàn)消息隊列的多種方法小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • SpringBoot整合Dubbo+Zookeeper實現(xiàn)RPC調(diào)用

    SpringBoot整合Dubbo+Zookeeper實現(xiàn)RPC調(diào)用

    這篇文章主要給大家介紹了Spring Boot整合Dubbo+Zookeeper實現(xiàn)RPC調(diào)用的步驟詳解,文中有詳細的代碼示例,對我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-07-07
  • Spring與bean有關(guān)的生命周期示例詳解

    Spring與bean有關(guān)的生命周期示例詳解

    這篇文章主要給大家介紹了關(guān)于Spring與bean有關(guān)的生命周期的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringMVC體系分層模式原理圖解

    SpringMVC體系分層模式原理圖解

    這篇文章主要介紹了SpringMVC體系分層模式原理圖解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot?實現(xiàn)CAS?Server統(tǒng)一登錄認證的詳細步驟

    SpringBoot?實現(xiàn)CAS?Server統(tǒng)一登錄認證的詳細步驟

    ??CAS(Central?Authentication?Service)中心授權(quán)服務(wù),是一個開源項目,目的在于為Web應(yīng)用系統(tǒng)提供一種可靠的單點登錄,這篇文章主要介紹了SpringBoot?實現(xiàn)CAS?Server統(tǒng)一登錄認證,需要的朋友可以參考下
    2024-02-02

最新評論