整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          php實現(xiàn)簡易驗證碼(僅供初學(xué)者測試玩玩)

          php結(jié)合html怎么實現(xiàn)驗證碼,這可能是很多人想知道的,尤其是php初學(xué)者。其實想要實現(xiàn)驗證碼的方式有很多種,但是對于初學(xué)者來說,有得可能過于復(fù)雜不適合新手。這里提供一個很簡易的,僅供初學(xué)者試玩,不要覺得瞧不起,再復(fù)雜的技術(shù)也是從一些簡單的代碼延伸出來的。

          1、首先新建一個php文件,輸入html基本模板,然后將下面這段php代碼插入body里:

          這段代碼首先聲明了編碼格式UTF-8,其次定義了一個空字符,接著通過字符串連接功能和mt_rand()隨機函數(shù),生成四個顏色隨機的隨機數(shù)。

          2、接下來就要寫html代碼

          這里要說一下:做一個隱藏的div保存生成的驗證碼,后面可以用來跟用戶輸入的進行比對。

          3、js驗證

          點擊提交按鈕,獲取到隱藏div里的文本值和輸入框的值,進行比較,判斷值是否一致,從而判斷驗證成功與否。

          當(dāng)然這只是給新手練手,提供一個思路用的。真正做網(wǎng)站驗證碼比這個復(fù)雜多,要考慮很多安全因素。

          天星期三了,如果不發(fā)生什么不可抗力事件,再過兩天就放假了。

          倒計時兩天

          不過不要高興太早,三天后就是年度最大電商節(jié)了,錢包是不是又要瘦身了。

          me too

          話說小編這兩天在忙著給項目做登錄界面,涉及到驗證碼,找了一下,發(fā)現(xiàn)現(xiàn)在網(wǎng)上大部分驗證碼都是后臺生成圖片,在前臺顯示,驗證也是在后臺驗證。小編就在想,只用前端技術(shù)能不能做出驗證碼呢?經(jīng)過一番努力,終于做出來了,先看大家一下效果圖

          感覺看著和圖片沒差

          下面給大家一步一步講解代碼:

          HTML代碼:

          <table>

          <tr>

          <td style="width: 70px;height: 40px;"><span >驗證碼:</span></td>

          /* 輸入框*/

          <td><input id="Text3" type="text" class="txtCode"/></td>

          /* 畫布,用來顯示驗證碼 */

          <td><canvas id="canvas" width="120" height="40"></canvas></td>

          </tr>

          </table>

          <script>

          document.getElementById("Text3").addEventListener("change",defined);

          //給輸入驗證碼的input添加監(jiān)聽事件,當(dāng)輸入框的值改變的時候,觸發(fā)defined()函數(shù)。

          var code = " ";

          function defined() {

          var text = document.getElementById("Text3").value.toUpperCase();

          //獲取輸入框的值,并用toUpperCase()將其轉(zhuǎn)化為大寫。

          function clearAndUpdate() {

          //定義clearAndUpdate()函數(shù)。用于在驗證碼錯誤的情況下刷新驗證碼和清空輸入框的值。

          document.getElementById("Text3").value = '';

          //清空輸入框的值。

          drawPic();

          調(diào)用drawPic(),刷新驗證碼。

          }

          //對驗證碼進行驗證。

          if(text.length < 0){//判斷為空的情況,彈出提示框。

          alert("請輸入驗證碼");

          }else if(text.length !==4){//判斷驗證碼位數(shù)不等于4的情況。

          alert("請輸入正確格式的驗證碼");

          clearAndUpdate();

          }else if(text == code){//比較驗證碼

          alert("通過驗證");

          }else{

          alert("驗證碼錯誤");//其他情況

          clearAndUpdate();

          }

          }

          下面是生成驗證碼的代碼,是利用畫布生成類似圖片的驗證碼。

          //生成一個隨機數(shù)

          function randomNum(min,max){

          return Math.floor( Math.random()*(max-min)+min);//在max和min之間生成隨機數(shù)。

          }

          //生成一個隨機色

          function randomColor(min,max){//采用rgb顏色,注意顏色是0-255。

          var r = randomNum(min,max);

          var g = randomNum(min,max);

          var b = randomNum(min,max);

          return "rgb("+r+","+g+","+b+")";

          }

          drawPic();

          //點擊驗證碼,則刷新驗證碼

          document.getElementById("canvas").onclick = function(e){

          e.preventDefault();

          drawPic();

          };

          //繪制驗證碼圖片

          function drawPic(){

          var canvas=document.getElementById("canvas");//獲取畫布容器

          var width=canvas.width;//分別獲取畫布的寬和高。

          var height=canvas.height;

          var ctx = canvas.getContext('2d');//獲取該canvas的2D繪圖環(huán)境對象

          ctx.textBaseline = 'bottom';設(shè)置文本基線是畫布的底部。

          //繪制背景色

          ctx.fillStyle = randomColor(200,240); //顏色若太深可能導(dǎo)致看不清

          ctx.fillRect(0,0,width,height);//畫出矩形,要記得ctx.fillStyle放在ctx.fillRect哦。

          //繪制文字

          var str = 'ABCEFGHJKLMNPQRSTWXY123456789';//選擇全部大寫字母和數(shù)字,這下知道為啥要把獲取的值轉(zhuǎn)化為大寫了吧。

          code = "";//定義一個變量code用于存儲生成的驗證碼。

          for(var i=0; i<4; i++){//這里i<4是生成4位數(shù)的驗證碼。

          var txt = str[randomNum(0,str.length)];//隨機獲取str的一個元素。

          code += txt;//將元素加入到code里。

          ctx.fillStyle = randomColor(50,160); //隨機生成字體顏色

          ctx.font = randomNum(15,30)+'px SimHei'; //隨機生成字體大小

          var x = 10+i*25;//元素在水平方向上的位置。

          var y = randomNum(25,35);//元素在豎直方向上的位置,盡量保持在中間,防止部分元素在畫布外。

          var deg = randomNum(-45, 45);//隨機生成旋轉(zhuǎn)角度。

          //修改坐標(biāo)原點和旋轉(zhuǎn)角度

          ctx.translate(x,y);//平移元素

          ctx.rotate(deg*Math.PI/180);//旋轉(zhuǎn)元素

          ctx.fillText(txt, 0,0);

          //恢復(fù)坐標(biāo)原點和旋轉(zhuǎn)角度

          ctx.rotate(-deg*Math.PI/180);

          ctx.translate(-x,-y);

          }

          //繪制干擾線

          for(var i=0; i<2; i++){

          ctx.strokeStyle = randomColor(40,180);//干擾線顏色。

          ctx.beginPath();//開始繪制。

          ctx.moveTo( randomNum(0,width), randomNum(0,height) );//起點位置

          ctx.lineTo( randomNum(0,width), randomNum(0,height) );//終點位置

          ctx.stroke();

          }

          /**繪制干擾點**/

          for(var i=0; i<50; i++){

          ctx.fillStyle = randomColor(0,255);

          ctx.beginPath();

          ctx.arc(randomNum(0,width),randomNum(0,height), 1, 0, 2*Math.PI);繪制點,下面說arc函數(shù)。

          ctx.fill();

          }

          }

          </script>

          arc() 方法創(chuàng)建弧/曲線

          context.arc(x,y,r,sAngle,eAngle,counterclockwise);

          x圓的中心的 x 坐標(biāo)。

          y圓的中心的 y 坐標(biāo)。

          r圓的半徑。

          sAngle起始角,以弧度計。(弧的圓形的三點鐘位置是 0 度)。

          eAngle結(jié)束角,以弧度計。

          counterclockwise可選。規(guī)定應(yīng)該逆時針還是順時針繪圖。False = 順時針,true = 逆時針。

          這就是全部的代碼了。

          在頁面上試一下,首先是隨機生成的驗證碼,

          開始輸入不是4位數(shù)的驗證碼,點擊確定后會刷新驗證碼。

          然后是4位數(shù),但是不正確的驗證碼,

          當(dāng)我們輸入正確的驗證碼時,

          這就是我做的驗證碼功能了,都是原生的js,沒有用后臺的知識,小伙伴有沒有理解哦。

          最近因為項目快要交付了,所有人都開始動員起來測試系統(tǒng),因為小編參與了整個的開發(fā)流程,所以很多同事遇到bug都來問我,需要在哪里改,東西寫在哪了。第一次知道改bug這么費精力,改了這個,又出來好幾個新的,簡直爆炸。

          打死也不承認是我寫的

          不說了,一會經(jīng)理要問我改多少bug了。

          不敢跑,不敢跑

          AJ-Captcha行為驗證碼,包含滑動拼圖、文字點選兩種方式,UI支持彈出和嵌入兩種方式。后端提供Java實現(xiàn),前端提供了php、angular、html、vue、uni-app、flutter、android、ios等代碼示例。


          源碼倉庫以及協(xié)議

          源碼地址:https://gitee.com/anji-plus/captcha

          基于apache2協(xié)議,開源免費商用,但需要保留版權(quán)聲明

          Apache 2.0 開源協(xié)議具有以下特點:

          1. 授權(quán):該協(xié)議允許任何人自由使用、復(fù)制、修改、分發(fā)和銷售被許可軟件的副本。

          2. 版權(quán)聲明:被許可軟件的副本必須包含原始版權(quán)聲明和許可聲明。

          3. 專利授權(quán):該協(xié)議授予了對軟件相關(guān)專利的非專屬授權(quán),這意味著使用該軟件的人不會受到專利侵權(quán)的指控。

          4. 責(zé)任限制:被許可軟件是按"原樣"提供的,沒有任何明示或暗示的擔(dān)保和條件。使用者對軟件的使用有責(zé)任承擔(dān)風(fēng)險。

          5. 分發(fā)修改版本:使用者可以基于被許可軟件創(chuàng)建衍生作品,并將其分發(fā)。然而,衍生作品必須遵循Apache 2.0協(xié)議,并包含相應(yīng)的版權(quán)聲明和許可聲明。

          總體而言,Apache 2.0 開源協(xié)議提供了靈活的許可方式,鼓勵創(chuàng)新和共享。它廣泛應(yīng)用于許多開源軟件項目,包括Apache HTTP服務(wù)器等。

          項目啟動

          • 后端工程

          提供了go、php、java(springboot、springmvc)多種版本,我們這里以springboot為例

          直接運行 StartApplication.java,配置文件先不修改 ,使用默認的,我們一會兒再來看看都有些什么配置項

          • 前端

          提供的版本有很多,我們這里以vue為例演示

          本地啟動執(zhí)行命令,記得修改接口地址

          npm install
          npm run dev

          啟動成功后可以看到演示界面

          功能體驗

          進入內(nèi)部頁面后,還有前端代碼集成示例,可以非常方便的集成到自己的項目中去

          技術(shù)細節(jié)

          • 整體時序圖

          整體時序圖

          • 后端的配置項

          captcha/service/springboot/src/resources/application.properties

          spring.application.name=captcha-service
          server.port=8080
          
          # 滑動驗證,底圖路徑,不配置將使用默認圖片
          # 支持全路徑
          # 支持項目路徑,以classpath:開頭,取resource目錄下路徑,例:classpath:images/jigsaw
          aj.captcha.jigsaw=classpath:images/jigsaw
          # 滑動驗證,底圖路徑,不配置將使用默認圖片
          # 支持全路徑
          # 支持項目路徑,以classpath:開頭,取resource目錄下路徑,例:classpath:images/pic-click
          aj.captcha.pic-click=classpath:images/pic-click
          
          # 對于分布式部署的應(yīng)用,我們建議應(yīng)用自己實現(xiàn)CaptchaCacheService,比如用Redis或者memcache,
          # 參考CaptchaCacheServiceRedisImpl.java
          # 如果應(yīng)用是單點的,也沒有使用redis,那默認使用內(nèi)存。
          # 內(nèi)存緩存只適合單節(jié)點部署的應(yīng)用,否則驗證碼生產(chǎn)與驗證在節(jié)點之間信息同步,導(dǎo)致失敗。
          # !!! 注意啦,如果應(yīng)用有使用spring-boot-starter-data-redis,
          # 請打開CaptchaCacheServiceRedisImpl.java注釋。
          # redis ----->  SPI: 在resources目錄新建META-INF.services文件夾(兩層),參考當(dāng)前服務(wù)resources。
          # 緩存local/redis...
          aj.captcha.cache-type=local
          # local緩存的閾值,達到這個值,清除緩存
          #aj.captcha.cache-number=1000
          # local定時清除過期緩存(單位秒),設(shè)置為0代表不執(zhí)行
          #aj.captcha.timing-clear=180
          #spring.redis.host=10.108.11.46
          #spring.redis.port=6379
          #spring.redis.password=
          #spring.redis.database=2
          #spring.redis.timeout=6000
          
          # 驗證碼類型default兩種都實例化。
          aj.captcha.type=default
          # 漢字統(tǒng)一使用Unicode,保證程序通過@value讀取到是中文,可通過這個在線轉(zhuǎn)換
          # https://tool.chinaz.com/tools/unicode.aspx 中文轉(zhuǎn)Unicode
          # 右下角水印文字(我的水印)
          aj.captcha.water-mark=我的水印
          # 右下角水印字體(不配置時,默認使用文泉驛正黑)
          # 由于宋體等涉及到版權(quán),我們jar中內(nèi)置了開源字體【文泉驛正黑】
          # 方式一:直接配置OS層的現(xiàn)有的字體名稱,比如:宋體
          # 方式二:自定義特定字體,請將字體放到工程resources下fonts文件夾,支持ttf\ttc\otf字體
          # aj.captcha.water-font=WenQuanZhengHei.ttf
          # 點選文字驗證碼的文字字體(文泉驛正黑)
          # aj.captcha.font-type=WenQuanZhengHei.ttf
          # 校驗滑動拼圖允許誤差偏移量(默認5像素)
          aj.captcha.slip-offset=5
          # aes加密坐標(biāo)開啟或者禁用(true|false)
          aj.captcha.aes-status=true
          # 滑動干擾項(0/1/2)
          aj.captcha.interference-options=2
          
          #點選字體樣式 默認Font.BOLD
          aj.captcha.font-style=1
          #點選字體字體大小
          aj.captcha.font-size=25
          #點選文字個數(shù),存在問題,暫不支持修改
          #aj.captcha.click-word-count=4
          
          
          aj.captcha.history-data-clear-enable=false
          
          # 接口請求次數(shù)一分鐘限制是否開啟 true|false
          aj.captcha.req-frequency-limit-enable=false
          # 驗證失敗5次,get接口鎖定
          aj.captcha.req-get-lock-limit=5
          # 驗證失敗后,鎖定時間間隔,s
          aj.captcha.req-get-lock-seconds=360
          # get接口一分鐘內(nèi)請求數(shù)限制
          aj.captcha.req-get-minute-limit=30
          # check接口一分鐘內(nèi)請求數(shù)限制
          aj.captcha.req-check-minute-limit=30
          # verify接口一分鐘內(nèi)請求數(shù)限制(暫用不上,可后臺直接調(diào)用captchaService)
          #aj.captcha.req-verify-minute-limit=30
          

          可以看到后端可以配置的選項還是很多的,可以切換緩存類型,展示底圖,接口限制等等

          • 接口分析

          我們嘗試滑動一次,發(fā)現(xiàn)在整個請求過程中,前端向后端發(fā)出了2個接口請求

          第1個是滑動停止時,帶著滑動碼類型和位置信息請求check校驗接口,如果滑動得是正確得位置,則里面得result字段為ture

          http://localhost:8080/captcha/check
          
          {"captchaType":"blockPuzzle","pointJson":"Ld/yNtPlENUtOoX2qdKHvNO5y/X8LU+vOUPWfitmrjc=","token":"3d5591a033d8482c89e0a77f477a9365"}
          
          {
          "repCode": "0000",
          "repMsg": null,
          "repData": {
          "captchaId": null,
          "projectCode": null,
          "captchaType": "blockPuzzle",
          "captchaOriginalPath": null,
          "captchaFontType": null,
          "captchaFontSize": null,
          "secretKey": null,
          "originalImageBase64": null,
          "point": null,
          "jigsawImageBase64": null,
          "wordList": null,
          "pointList": null,
          "pointJson": "Ld/yNtPlENUtOoX2qdKHvNO5y/X8LU+vOUPWfitmrjc=",
          "token": "3d5591a033d8482c89e0a77f477a9365",
          "result": true,
          "captchaVerification": null,
          "clientUid": null,
          "ts": null,
          "browserInfo": null
          },
          "success": true
          }

          第2個其實是獲取圖片驗證碼得請求,是因為第一個我們滑動到了正確位置,所以界面刷新又獲取了一次新得圖片驗證碼

          http://localhost:8080/captcha/get

          實際上真正做滑動校驗得是/check 這個接口

          src/main/java/com/anji/captcha/service/impl/BlockPuzzleCaptchaServiceImpl.java

          public ResponseModel check(CaptchaVO captchaVO) {
          		ResponseModel r = super.check(captchaVO);
          		if(!validatedReq(r)){
          			return r;
          		}
                  //取坐標(biāo)信息
                  String codeKey = String.format(REDIS_CAPTCHA_KEY, captchaVO.getToken());
                  if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
                      return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_INVALID);
                  }
                  String s = CaptchaServiceFactory.getCache(cacheType).get(codeKey);
                  //驗證碼只用一次,即刻失效
                  CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
                  PointVO point = null;
                  PointVO point1 = null;
                  String pointJson = null;
                  try {
                      point = JsonUtil.parseObject(s, PointVO.class);
                      //aes解密
                      pointJson = decrypt(captchaVO.getPointJson(), point.getSecretKey());
                      point1 = JsonUtil.parseObject(pointJson, PointVO.class);
                  } catch (Exception e) {
                      logger.error("驗證碼坐標(biāo)解析失敗", e);
                      afterValidateFail(captchaVO);
                      return ResponseModel.errorMsg(e.getMessage());
                  }
                  if (point.x - Integer.parseInt(slipOffset) > point1.x
                          || point1.x > point.x + Integer.parseInt(slipOffset)
                          || point.y != point1.y) {
                      afterValidateFail(captchaVO);
                      return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR);
                  }
                  //校驗成功,將信息存入緩存
                  String secretKey = point.getSecretKey();
                  String value = null;
                  try {
                      value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey);
                  } catch (Exception e) {
                      logger.error("AES加密失敗", e);
                      afterValidateFail(captchaVO);
                      return ResponseModel.errorMsg(e.getMessage());
                  }
                  String secondKey = String.format(REDIS_SECOND_CAPTCHA_KEY, value);
                  CaptchaServiceFactory.getCache(cacheType).set(secondKey, captchaVO.getToken(), EXPIRESIN_THREE);
                  captchaVO.setResult(true);
                  captchaVO.resetClientFlag();
                  return ResponseModel.successData(captchaVO);
              }
          

          分析一下接口的實現(xiàn),經(jīng)過了坐標(biāo)解密(滑塊圖的坐標(biāo)信息是通過加密后傳輸給前端),坐標(biāo)對比,需要滑動的位置在允許的范圍內(nèi)才認為滑動位置正確

          再來看看驗證碼生成的實現(xiàn)

          src/main/java/com/anji/captcha/service/impl/BlockPuzzleCaptchaServiceImpl.java

          public ResponseModel get(CaptchaVO captchaVO) {
          		ResponseModel r = super.get(captchaVO);
          		if(!validatedReq(r)){
          			return r;
          		}
                  //原生圖片
                  BufferedImage originalImage = ImageUtils.getOriginal();
                  if (null == originalImage) {
                      logger.error("滑動底圖未初始化成功,請檢查路徑");
                      return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL);
                  }
                  //設(shè)置水印
                  Graphics backgroundGraphics = originalImage.getGraphics();
                  int width = originalImage.getWidth();
                  int height = originalImage.getHeight();
                  backgroundGraphics.setFont(waterMarkFont);
                  backgroundGraphics.setColor(Color.white);
                  backgroundGraphics.drawString(waterMark, width - getEnOrChLength(waterMark), height - (HAN_ZI_SIZE / 2) + 7);
          
                  //摳圖圖片
                  String jigsawImageBase64 = ImageUtils.getslidingBlock();
                  BufferedImage jigsawImage = ImageUtils.getBase64StrToImage(jigsawImageBase64);
                  if (null == jigsawImage) {
                      logger.error("滑動底圖未初始化成功,請檢查路徑");
                      return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_BASEMAP_NULL);
                  }
                  CaptchaVO captcha = pictureTemplatesCut(originalImage, jigsawImage, jigsawImageBase64);
                  if (captcha == null
                          || StringUtils.isBlank(captcha.getJigsawImageBase64())
                          || StringUtils.isBlank(captcha.getOriginalImageBase64())) {
                      return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_ERROR);
                  }
                  return ResponseModel.successData(captcha);
              }

          大致過程:加載驗證碼底圖、隨機從底圖中扣取一個部分作為要滑動的小圖,最后把兩個圖都以base64圖片形式返回給前端

          總結(jié)

          1、本篇我們介紹了一個開源免費的行為驗證碼的項目

          2、完成了項目的實際搭建和功能體驗

          3、我們初步分析了后端的核心代碼,了解了滑動驗證碼的生成和校驗邏輯

          4、整體來說這個項目非常完整,并且提供了很多的終端實現(xiàn)(常見的基本上都支持了,html,vue,小程序終端,原生ios android等),并且后端也是非常容易集成到自己的項目,拿著demo版本修改一下即可


          主站蜘蛛池模板: 无码人妻精品一区二区三区99不卡| 极品人妻少妇一区二区三区| 日韩人妻无码一区二区三区久久99 | 色婷婷亚洲一区二区三区| 国产未成女一区二区三区 | 美女福利视频一区| 精品乱码一区二区三区四区| 视频在线观看一区| 亚洲福利一区二区精品秒拍| 亚洲AV成人精品日韩一区18p | 日本精品一区二区三区在线视频一 | 午夜福利国产一区二区| 波多野结衣一区二区三区高清在线| 日韩一区二区久久久久久| 一区视频免费观看| 日本一道高清一区二区三区| 精品无码一区二区三区亚洲桃色| 中文字幕人妻无码一区二区三区| 无码国产精品一区二区免费I6| 亚洲一区二区三区在线观看网站 | 国产精品久久无码一区二区三区网| 福利一区二区在线| 亚洲视频一区在线| 精品一区二区三区影院在线午夜| 日韩A无码AV一区二区三区| 免费视频精品一区二区三区| 人妻少妇精品视频一区二区三区| 国产一区二区成人| 东京热无码av一区二区| 在线观看国产区亚洲一区成人| 日韩人妻精品一区二区三区视频| 日本午夜精品一区二区三区电影| 国产肥熟女视频一区二区三区| 国产色精品vr一区区三区| 黑巨人与欧美精品一区| 亚欧色一区W666天堂| 中文字幕无码免费久久9一区9| 欧美日本精品一区二区三区| 国语精品一区二区三区| 亚洲欧美国产国产综合一区| 国产成人片视频一区二区|