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等代碼示例。
源碼地址: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)部頁面后,還有前端代碼集成示例,可以非常方便的集成到自己的項目中去
整體時序圖
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圖片形式返回給前端
1、本篇我們介紹了一個開源免費的行為驗證碼的項目
2、完成了項目的實際搭建和功能體驗
3、我們初步分析了后端的核心代碼,了解了滑動驗證碼的生成和校驗邏輯
4、整體來說這個項目非常完整,并且提供了很多的終端實現(xiàn)(常見的基本上都支持了,html,vue,小程序終端,原生ios android等),并且后端也是非常容易集成到自己的項目,拿著demo版本修改一下即可
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。