<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

SpringBoot分布式驗證碼登錄方案

jf_ro2CN3Fa ? 來源:碼畜君 ? 2023-10-12 17:34 ? 次閱讀

前言

為了防止世界被破壞,為了守護世界的和平。。。說錯了,重來~

為了防止驗證系統被暴力破解,很多系統都增加了驗證碼效驗,比較常見的就是圖片二維碼,業內比較安全的是短信驗證碼,當然還有一些拼圖驗證碼,加入人工智能的二維碼等等,我們今天的主題就是前后端分離的圖片二維碼登錄方案。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

前后端未分離的驗證碼登錄方案

傳統的項目大都是基于session交互的,前后端都在一個項目里面,比如傳統的SSH項目或者一些JSP系統,當前端頁面觸發到獲取驗證碼請求,可以將驗證碼里面的信息存在上下文中,所以登錄的時候只需要 用戶名、密碼、驗證碼即可。

驗證碼生成流程如下

7bb22bda-68d7-11ee-939d-92fbcf53809c.jpg

登錄驗證流程如下

7bc315d0-68d7-11ee-939d-92fbcf53809c.jpg

可以發現,整個登錄流程還是依賴session上下文的,并且由后端調整頁面。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

前后端分離的驗證碼登錄方案

隨著系統和業務的不停升級,前后端代碼放在一起的項目越來越臃腫,已經無法快速迭代和職責區分了,于是紛紛投入了前后端分離的懷抱,發現代碼和職責分離以后,開發效率越來越高了,功能迭代還越來越快,但是以前的驗證碼登錄方案就要更改了。

驗證碼生成流程如下

7bcd88a8-68d7-11ee-939d-92fbcf53809c.jpg

對比原來的方案,增加了redis中間件,不再是存在session里面了,但是后面怎么區分這個驗證碼是這個請求生成的呢?所以我們加入了唯一標識符來區分

登錄驗證流程如下

7bda2694-68d7-11ee-939d-92fbcf53809c.jpg

可以發現,基于前后端分離的分布式項目登錄方案對比原來,加了一個redis中間件和token返回,不再依賴上下文session,并且頁面調整也是由后端換到了前端

動手擼輪子

基于驗證碼的輪子還是挺多的,本文就以Kaptcha這個項目為例,通過springboot項目集成Kaptcha來實現驗證碼生成和登錄方案。

Kaptcha介紹

Kaptcha是一個基于SimpleCaptcha的驗證碼開源項目

我找的這個輪子是基于SimpleCaptcha二次封裝的,maven依賴如下


<dependency>
<groupId>com.github.pengglegroupId>
<artifactId>kaptchaartifactId>
<version>2.3.2version>
dependency>

新建項目并加入依賴

依賴主要有 SpringBoot、Kaptcha、Redis

pom.xml

<projectxmlns="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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>

<groupId>com.lzpgroupId>
<artifactId>kaptchaartifactId>
<version>1.0-SNAPSHOTversion>

<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.0.RELEASEversion>
<relativePath/>
parent>

<dependencies>

<dependency>
<groupId>com.github.pengglegroupId>
<artifactId>kaptchaartifactId>
<version>2.3.2version>
dependency>

<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>


<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>


<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>

<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.3version>
dependency>

<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
dependency>

dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>

project>

Redis配置類RedisConfig

@Configuration
publicclassRedisConfig{

@Bean
publicRedisTemplateredisTemplate(LettuceConnectionFactoryredisConnectionFactory){
RedisTemplateredisTemplate=newRedisTemplate();
redisTemplate.setKeySerializer(newStringRedisSerializer());
redisTemplate.setValueSerializer(newGenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(newStringRedisSerializer());
redisTemplate.setHashValueSerializer(newGenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
returnredisTemplate;
}

}

驗證碼配置類KaptchaConfig

@Configuration
publicclassKaptchaConfig{
@Bean
publicDefaultKaptchaproducer(){

DefaultKaptchadefaultKaptcha=newDefaultKaptcha();
Propertiesproperties=newProperties();
properties.setProperty("kaptcha.border","no");
properties.setProperty("kaptcha.border.color","105,179,90");
properties.setProperty("kaptcha.textproducer.font.color","black");
properties.setProperty("kaptcha.image.width","110");
properties.setProperty("kaptcha.image.height","40");
properties.setProperty("kaptcha.textproducer.char.string","23456789abcdefghkmnpqrstuvwxyzABCDEFGHKMNPRSTUVWXYZ");
properties.setProperty("kaptcha.textproducer.font.size","30");
properties.setProperty("kaptcha.textproducer.char.space","3");
properties.setProperty("kaptcha.session.key","code");
properties.setProperty("kaptcha.textproducer.char.length","4");
properties.setProperty("kaptcha.textproducer.font.names","宋體,楷體,微軟雅黑");
//properties.setProperty("kaptcha.obscurificator.impl","com.xxx");可以重寫實現類
properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");
Configconfig=newConfig(properties);
defaultKaptcha.setConfig(config);

returndefaultKaptcha;
}

驗證碼控制層CaptchaController

為了方便代碼寫一塊了,講究看

packagecom.lzp.kaptcha.controller;

importcom.google.code.kaptcha.impl.DefaultKaptcha;
importcom.lzp.kaptcha.service.CaptchaService;
importcom.lzp.kaptcha.vo.CaptchaVO;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.ResponseBody;
importorg.springframework.web.bind.annotation.RestController;
importsun.misc.BASE64Encoder;

importjavax.imageio.ImageIO;
importjava.awt.image.BufferedImage;
importjava.io.ByteArrayOutputStream;
importjava.io.IOException;

@RestController
@RequestMapping("/captcha")
publicclassCaptchaController{

@Autowired
privateDefaultKaptchaproducer;

@Autowired
privateCaptchaServicecaptchaService;

@ResponseBody
@GetMapping("/get")
publicCaptchaVOgetCaptcha()throwsIOException{

//生成文字驗證碼
Stringcontent=producer.createText();
//生成圖片驗證碼
ByteArrayOutputStreamoutputStream=null;
BufferedImageimage=producer.createImage(content);

outputStream=newByteArrayOutputStream();
ImageIO.write(image,"jpg",outputStream);
//對字節數組Base64編碼
BASE64Encoderencoder=newBASE64Encoder();

Stringstr="data:image/jpeg;base64,";
Stringbase64Img=str+encoder.encode(outputStream.toByteArray()).replace("
","").replace("
","");

CaptchaVOcaptchaVO=captchaService.cacheCaptcha(content);
captchaVO.setBase64Img(base64Img);

returncaptchaVO;
}

}

驗證碼返回對象CaptchaVO

packagecom.lzp.kaptcha.vo;

publicclassCaptchaVO{
/**
*驗證碼標識符
*/
privateStringcaptchaKey;
/**
*驗證碼過期時間
*/
privateLongexpire;
/**
*base64字符串
*/
privateStringbase64Img;

publicStringgetCaptchaKey(){
returncaptchaKey;
}

publicvoidsetCaptchaKey(StringcaptchaKey){
this.captchaKey=captchaKey;
}

publicLonggetExpire(){
returnexpire;
}

publicvoidsetExpire(Longexpire){
this.expire=expire;
}

publicStringgetBase64Img(){
returnbase64Img;
}

publicvoidsetBase64Img(Stringbase64Img){
this.base64Img=base64Img;
}
}

Redis封裝類 RedisUtils

網上隨意找的,類里面注明來源,將就用,代碼較多就不貼了,文末有代碼獲取

驗證碼方法層CaptchaService

packagecom.lzp.kaptcha.service;

importcom.lzp.kaptcha.utils.RedisUtils;
importcom.lzp.kaptcha.vo.CaptchaVO;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Value;
importorg.springframework.stereotype.Service;

importjava.util.UUID;

@Service
publicclassCaptchaService{

@Value("${server.session.timeout:300}")
privateLongtimeout;

@Autowired
privateRedisUtilsredisUtils;


privatefinalStringCAPTCHA_KEY="captcha";

publicCaptchaVOcacheCaptcha(Stringcaptcha){
//生成一個隨機標識符
StringcaptchaKey=UUID.randomUUID().toString();

//緩存驗證碼并設置過期時間
redisUtils.set(CAPTCHA_KEY.concat(captchaKey),captcha,timeout);

CaptchaVOcaptchaVO=newCaptchaVO();
captchaVO.setCaptchaKey(captchaKey);
captchaVO.setExpire(timeout);

returncaptchaVO;
}

}

用戶登錄對象封裝LoginDTO

packagecom.lzp.kaptcha.dto;

publicclassLoginDTO{

privateStringuserName;

privateStringpwd;

privateStringcaptchaKey;

privateStringcaptcha;

publicStringgetUserName(){
returnuserName;
}

publicvoidsetUserName(StringuserName){
this.userName=userName;
}

publicStringgetPwd(){
returnpwd;
}

publicvoidsetPwd(Stringpwd){
this.pwd=pwd;
}

publicStringgetCaptchaKey(){
returncaptchaKey;
}

publicvoidsetCaptchaKey(StringcaptchaKey){
this.captchaKey=captchaKey;
}

publicStringgetCaptcha(){
returncaptcha;
}

publicvoidsetCaptcha(Stringcaptcha){
this.captcha=captcha;
}
}

登錄控制層UserController

這塊我寫邏輯代碼了,相信大家都看的懂

packagecom.lzp.kaptcha.controller;

importcom.lzp.kaptcha.dto.LoginDTO;
importcom.lzp.kaptcha.utils.RedisUtils;
importcom.lzp.kaptcha.vo.UserVO;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
publicclassUserController{

@Autowired
privateRedisUtilsredisUtils;

@PostMapping("/login")
publicUserVOlogin(@RequestBodyLoginDTOloginDTO){
Objectcaptch=redisUtils.get(loginDTO.getCaptchaKey());
if(captch==null){
//throw驗證碼已過期
}
if(!loginDTO.getCaptcha().equals(captch)){
//throw驗證碼錯誤
}
//查詢用戶信息

//判斷用戶是否存在不存在拋出用戶名密碼錯誤

//判斷密碼是否正確,不正確拋出用戶名密碼錯誤

//構造返回到前端的用戶對象并封裝信息和生成token

returnnewUserVO();
}
}

驗證碼獲取和查看

7be97284-68d7-11ee-939d-92fbcf53809c.jpg7bf6213c-68d7-11ee-939d-92fbcf53809c.jpg


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 代碼
    +關注

    關注

    30

    文章

    4567

    瀏覽量

    66998
  • Redis
    +關注

    關注

    0

    文章

    365

    瀏覽量

    10529
  • SpringBoot
    +關注

    關注

    0

    文章

    172

    瀏覽量

    112

原文標題:SpringBoot 分布式驗證碼登錄方案

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    用基于gin框架的Go語言來實現手機號發送短信驗證碼登錄

    現在大多數app或wap都實現了通過手機號獲取驗證碼進行驗證登錄,下面來看下用go來實現手機號發送短信驗證碼登錄的過程,基于的框架是gin
    的頭像 發表于 07-20 09:36 ?3912次閱讀
    用基于gin框架的Go語言來實現手機號發送短信<b class='flag-5'>驗證碼</b><b class='flag-5'>登錄</b>

    鴻蒙原生應用/元服務開發-Serverless賬戶驗證碼的問題

    在應用/元服務早期使用過程中,-Serverless賬戶驗證碼的格式是[AGC][應用/元服務名稱],如下圖。 但是,在最近,[應用/元服務]名稱直接變成了【default】,用戶收到這種驗證碼后,心里存有疑慮的,這是哪里配置或者設置的問題嗎?大家有遇到同樣的問題嗎?如何
    發表于 12-27 15:55

    分布式軟件系統

    。更重要的是,NI LabVIEW 8的分布式智能提供的解決方案不僅令這些挑戰迎刃而解,且易于實施。LabVIEW 8的分布式智能具體包括: 可對分布式系統中的所有結點編程——包括主機
    發表于 07-22 14:53

    織夢dedecms登陸提示“驗證碼不正確”的完整解決方案

    != $svali)替換為:if( false)然后,在模板dede/templets/login.htm里去掉以下驗證碼的具體HTML代碼:驗證碼: 以上辦法可以解決dedecms登錄時提示
    發表于 03-10 23:53

    10種意想不到的驗證碼風格設計

    QapTchaQapTcha–拖曳jQuery驗證碼系統。作為用戶,我們只需移動滾動條。2. buttonCaptcha這個要復雜點,需要拖動目標字母。3. Fancy Draggable
    發表于 05-05 14:03

    什么時候才能發帖不需要驗證碼?

    每次發帖都要驗證碼,什么時候才能發帖不需要驗證碼?
    發表于 06-04 19:17

    12306圖片驗證碼難倒了誰?

    ,現在不光要靠網速……(還有看人品)下面是摘自網上的新聞:  一次性輸入正確率僅為8%  為了防止搶票軟件破解,鐵道部12306網站不斷將登錄的圖形驗證碼更新,層出不窮的圖形碼讓一些網購車票的旅客
    發表于 12-08 10:29

    論壇注冊時不顯示驗證碼

    論壇注冊時不顯示驗證碼啊,提交時提示驗證碼錯誤,希望早點解決,這是用別人的號發的?。。。。。。。。。?!
    發表于 03-01 21:45

    無法驗證郵箱,總是提示驗證碼錯誤,驗證碼明明是正確的。

    `無法驗證郵箱,總是提示驗證碼錯誤,驗證碼明明是正確的。是不是系統的bug?`
    發表于 05-12 10:41

    為什么短信驗證碼在我們生活中頻頻出現

    隨著時代的進步和科學技術的發展,越來越多的企業已經離不開短信驗證碼發送平臺了,因為它可以幫助企業處理大量的用戶信息,提高接收短信驗證碼的經驗意識。大型網站提供了短信驗證碼功能,更準確、更安全地保證了
    發表于 04-16 23:12

    如何高效完成HarmonyOS分布式應用測試?

    分布式應用涉及多臺設備協同時,由于缺乏全面且高效的隱私合規檢測方案,安全隱私問題攔截難度較大。鑒于以上HarmonyOS分布式應用測試面臨的挑戰,華為DevEco Testing提供了一套
    發表于 12-13 18:07

    怎樣去設計一個基于springboot+freemark+jpa+MySQL的在線電影訂票系統

    實現的功能有:前臺: 1、正在上映的電影瀏覽查看,可在線播放預告視頻。 2、影院信息瀏覽查看,包括各影院上映場次數。 3、新聞咨詢信息瀏覽查看。 4、地域信息查看切換。 5、用戶注冊登錄,登錄支持發送短信驗證碼
    發表于 01-03 07:22

    一文解析驗證碼與打平臺的攻防對抗

    1、驗證碼與打平臺的攻防對抗  眾所周知,驗證碼的出現是為了區分人和機器,但隨著科技的發展,黑產從業者的可圖之利增多,驗證碼的戰場也進入了一段破解與抗破解的持久博弈?! ?/div>
    發表于 09-28 11:02

    平臺是如何高效的破解市面上各家驗證碼平臺的各種形式驗證碼的?

    眾所周知,驗證碼的出現是為了區分人和機器,但隨著科技的發展,黑產從業者的可圖之利增多,驗證碼的戰場也進入了一段破解與抗破解的持久博弈。驗證碼在逐漸復雜的同時,黑灰產的攻擊手段也不斷提升。本文就從
    發表于 11-01 15:21

    以一個真實網站的驗證碼為例,實現了基于一下KNN的驗證碼識別

    很多網站登錄都需要輸入驗證碼,如果要實現自動登錄就不可避免的要識別驗證碼。本文以一個真實網站的驗證碼為例,實現了基于一下KNN的
    的頭像 發表于 12-24 17:27 ?7456次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>