<var id="k6qs5"><sup id="k6qs5"></sup></var>

  • 全棧小白的gravatar頭像
    全棧小白 2023-11-06 14:26:51
    項目中登錄驗證碼怎么做才合理

    嘮嗑部分

    今天我們來聊聊項目實戰中登錄驗證碼如何做比較合理,首先我們聊以下幾個問題

    1、登錄時驗證碼校驗是否必要?

    答案當然是很有必要的,因為用戶登錄行為會直接影響數據庫,如果沒有某些防范措施,有惡意用戶暴力破解密碼,那就十分嚴重了,驗證碼校驗是防止接口被刷的一種方式,可以大大的降低接口請求的頻率

    還有一些系統會加入密碼次數校驗,當密碼錯誤次數超過n次時,會鎖定該用戶,在指定時間段內不允許登錄

    除此之外,對于用戶接口限流也是一種方案,小伙伴可以自行嘗試

    2、驗證碼校驗是前端行為還是服務端行為?

    兩種方式均可以,區別如下

    前端行為:僅能保證前端頁面操作時驗證的效果,不能夠保證接口安全,用戶可跳過頁面直接訪問服務端接口,不建議

    服務端行為:能夠保證登陸接口安全,限制用戶登錄頻率,可配合用戶ip流量限制、密碼錯誤次數防范等措施,建議

    3、生成驗證碼后,服務器應該存在什么地方,會有哪些問題?

    在學習jsp+servlet時,用戶信息通常會存入到服務端的session當中,驗證碼也可存入session,但是之針對單實例可行,如多實例,需實現session共享或配合ip_hash的規則

    可存入分布式緩存中,不會有多實例問題、也不需要解決session共享的問題

    4、如何實現分布式驗證碼校驗,保證登錄安全?

    首先,服務端提供獲取驗證碼的接口,出參包含key、code(code為實際驗證碼,key為Redis存儲的key),返回驗證碼之前將驗證碼存入redis中

    前端收到驗證碼后,采用canvas繪制驗證碼,用戶手動輸入驗證碼后,將用戶名、密碼、驗證碼、key傳入服務端進行登錄

    服務端收到登錄請求后,查詢redis,跟用戶輸入的驗證碼進行比較,比較通過后進行登錄

    言歸正傳

    驗證碼校驗是用戶登錄的第一道屏障,未驗證通過則不進行登錄,不接觸數據庫

    1、導入驗證碼依賴,其他依賴自行添加,需redis、hutool-all

    <dependency>
        <groupId>com.github.penggle</groupId>
        <artifactId>kaptcha</artifactId>
        <version>2.3.2</version>
    </dependency>

    2、配置kaptcha

    /*
     * @Project:cxs-currency-sys-server
     * @Author:cxs
     * @Motto:放下雜念,只為迎接明天更好的自己
     * */
    @Configuration
    public class KaptchaConfig {
        
        private static final String CHAR_STR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        private static final String CHAR_LENGTH = "4";
    ?
        @Bean
        public Producer captcha() {
            // 配置圖形驗證碼的基本參數
            Properties properties = new Properties();
            // 字符集
            properties.setProperty("kaptcha.textproducer.char.string", CHAR_STR);
            // 字符長度
            properties.setProperty("kaptcha.textproducer.char.length", CHAR_LENGTH);
            Config config = new Config(properties);
            DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
            defaultKaptcha.setConfig(config);
            return defaultKaptcha;
        }
    }

    3、生成驗證碼核心代碼

    public void getValidateCode(BaseResult result) {
            long startTime = System.currentTimeMillis();
            try {
                // 創建驗證碼文本
                ValidateCodeVO vo = new ValidateCodeVO();
                String key = IdUtil.simpleUUID();
                String code = captchaProducer.createText();
                redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.VALIDATE_CODE_PREFIX, key), code, commonConfig.getCodePeriod(), TimeUnit.SECONDS);
                vo.setKey(key);
                vo.setCode(code);
                result.setData(vo);
            } catch (Exception e) {
                log.error("生成驗證碼失敗,{}", e);
                result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
                result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg());
            } finally {
                long endTime = System.currentTimeMillis();
                log.info("【{}】【生成驗證碼接口】【{}ms】 \n入參:{}\n出參:{}", "生成驗證碼", endTime - startTime, null, result);
            }
        }

    4、接口出參預覽

    {
      "code": 200,
      "data": {
        "code": "2972",
        "key": "a23030e821384684a47912fac215cfb1"
      },
      "msg": "操作成功"
    }

    5、前端實現主要代碼(注意:登錄框的部分代碼)

    <el-form-item prop="code">
        <el-row :gutter="20">
            <el-col :span="12">
                <el-input ref="code" type="text" prefix-icon="el-icon-key" v-model="userInfo.code" placeholder="驗證碼" name="code" tabindex="2" autocomplete="on" @keyup.enter.native="loginHandle"/>
             </el-col>
            <el-col :span="6" :offset="3">
                <canvas ref="canvas" :width="codeInfo.codeWidth" :height="codeInfo.codeHeight" @click="reloadCode"></canvas>
            </el-col>
        </el-row>
    </el-form-item>

    6、需要到的函數

    // 創建驗證碼
    async createdCode() {
        // 調用上述接口生成
          const res = await getValidateCode()
          const {code, data, msg} = res
          this.userInfo.codeKey = data.key
          const identifyCode = data.code
          const codeList = identifyCode.split("");
          const canvas = this.$refs.canvas;// 獲取 canvas 元素
          const ctx = canvas.getContext('2d');
          ctx.textBaseline = 'bottom';
          if (this.backgroundColor != '' && this.backgroundColor != null) {// 繪制畫布背景顏色
            ctx.fillStyle = this.backgroundColor;
          } else {
            ctx.fillStyle = this.randomColor(255, 255);
          }
          ctx.fillRect(0, 0, this.contentWidth, this.contentHeight);
          codeList.forEach((code, i) => {// 繪制驗證碼字符
            this.drawText(ctx, code, i + 1, identifyCode.length);
          })
          this.drawLine(ctx);// 繪制干擾線
          this.drawDot(ctx);// 繪制干擾點
        },
        randomNum(min, max) {// 生成指定范圍內的隨機整數
          return Math.floor(Math.random() * (max - min) + min);
        },
        randomColor(min, max) {// 生成指定范圍內的隨機顏色
          const r = this.randomNum(min, max);
          const g = this.randomNum(min, max);
          const b = this.randomNum(min, max);
          return `rgb(${r},${g},${b})`;
        },
        drawText(ctx, txt, i, len) {// 繪制驗證碼字符
          ctx.fillStyle = this.randomColor(0, 160);
          ctx.font = `${this.randomNum(25, 30)}px SimHei`;
          const x = (i / (len + 1)) * 120;
          const y = this.randomNum(30, 35);
          const deg = this.randomNum(-45, 45);
          ctx.translate(x, y);
          ctx.rotate((deg * Math.PI) / 180);
          ctx.fillText(txt, 0, 0);
          ctx.rotate((-deg * Math.PI) / 180);
          ctx.translate(-x, -y);
        },
        drawLine(ctx) {// 繪制干擾線
          for (let i = 0; i < 5; i++) {
            ctx.strokeStyle = this.randomColor(100, 255);
            ctx.beginPath();
            ctx.moveTo(this.randomNum(0, 120), this.randomNum(0, 40));
            ctx.lineTo(this.randomNum(0, 120), this.randomNum(0, 40));
            ctx.stroke();
          }
        },
        drawDot(ctx) {// 繪制干擾點
          for (let i = 0; i < 80; i++) {
            ctx.fillStyle = this.randomColor(0, 255);
            ctx.beginPath();
            ctx.arc(this.randomNum(0, 120), this.randomNum(0, 40), 1, 0, 2 * Math.PI);
            ctx.fill();
          }
        },
        reloadCode() {// 點擊按鈕時清除畫布并重新生成驗證碼
          const canvas = this.$refs.canvas
          const ctx = canvas.getContext('2d')
          ctx.clearRect(0, 0, canvas.width, canvas.height)
          this.createdCode()
        },

    7、效果展示

    項目中登錄驗證碼怎么做才合理

    結語

    1、驗證碼功能就實現了,快去給你的項目安排吧!

    2、制作不易,一鍵四連再走吧,您的支持永遠是我最大的動力!


    打賞

    已有1人打賞

    最代碼官方的gravatar頭像
    最近瀏覽
    做你的景天  LV6 2月22日
    周青松197 2月21日
    暫無貢獻等級
    youwuzuichen  LV10 2月18日
    張張張飛  LV1 1月31日
    liiiyou  LV1 1月26日
    小學生波波  LV19 1月19日
    Gin19960217  LV3 1月16日
    JOEH60  LV10 1月12日
    qibodong  LV14 2023年12月14日
    Magic丶M  LV6 2023年12月7日
    頂部 客服 微信二維碼 底部
    >掃描二維碼關注最代碼為好友掃描二維碼關注最代碼為好友
    国产 国产 高清|护士奶头又白又大又好摸|中文字幕av一区二区三区|午夜亚洲国产理论片2020
    <var id="k6qs5"><sup id="k6qs5"></sup></var>