php结合html怎么实现验证码,这可能是很多人想知道的,尤其是php初学者。其实想要实现验证码的方式有很多种,但是对于初学者来说,有得可能过于复杂不适合新手。这里提供一个很简易的,仅供初学者试玩,不要觉得瞧不起,再复杂的技术也是从一些简单的代码延伸出来的。
1、首先新建一个php文件,输入html基本模板,然后将下面这段php代码插入body里:
这段代码首先声明了编码格式UTF-8,其次定义了一个空字符,接着通过字符串连接功能和mt_rand()随机函数,生成四个颜色随机的随机数。
2、接下来就要写html代码
这里要说一下:做一个隐藏的div保存生成的验证码,后面可以用来跟用户输入的进行比对。
3、js验证
点击提交按钮,获取到隐藏div里的文本值和输入框的值,进行比较,判断值是否一致,从而判断验证成功与否。
当然这只是给新手练手,提供一个思路用的。真正做网站验证码比这个复杂多,要考虑很多安全因素。
天星期三了,如果不发生什么不可抗力事件,再过两天就放假了。
倒计时两天
不过不要高兴太早,三天后就是年度最大电商节了,钱包是不是又要瘦身了。
me too
话说小编这两天在忙着给项目做登录界面,涉及到验证码,找了一下,发现现在网上大部分验证码都是后台生成图片,在前台显示,验证也是在后台验证。小编就在想,只用前端技术能不能做出验证码呢?经过一番努力,终于做出来了,先看大家一下效果图
感觉看着和图片没差
下面给大家一步一步讲解代码:
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添加监听事件,当输入框的值改变的时候,触发defined()函数。
var code = " ";
function defined() {
var text = document.getElementById("Text3").value.toUpperCase();
//获取输入框的值,并用toUpperCase()将其转化为大写。
function clearAndUpdate() {
//定义clearAndUpdate()函数。用于在验证码错误的情况下刷新验证码和清空输入框的值。
document.getElementById("Text3").value = '';
//清空输入框的值。
drawPic();
调用drawPic(),刷新验证码。
}
//对验证码进行验证。
if(text.length < 0){//判断为空的情况,弹出提示框。
alert("请输入验证码");
}else if(text.length !==4){//判断验证码位数不等于4的情况。
alert("请输入正确格式的验证码");
clearAndUpdate();
}else if(text == code){//比较验证码
alert("通过验证");
}else{
alert("验证码错误");//其他情况
clearAndUpdate();
}
}
下面是生成验证码的代码,是利用画布生成类似图片的验证码。
//生成一个随机数
function randomNum(min,max){
return Math.floor( Math.random()*(max-min)+min);//在max和min之间生成随机数。
}
//生成一个随机色
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绘图环境对象
ctx.textBaseline = 'bottom';设置文本基线是画布的底部。
//绘制背景色
ctx.fillStyle = randomColor(200,240); //颜色若太深可能导致看不清
ctx.fillRect(0,0,width,height);//画出矩形,要记得ctx.fillStyle放在ctx.fillRect哦。
//绘制文字
var str = 'ABCEFGHJKLMNPQRSTWXY123456789';//选择全部大写字母和数字,这下知道为啥要把获取的值转化为大写了吧。
code = "";//定义一个变量code用于存储生成的验证码。
for(var i=0; i<4; i++){//这里i<4是生成4位数的验证码。
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);//随机生成旋转角度。
//修改坐标原点和旋转角度
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);
}
//绘制干扰线
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函数。
ctx.fill();
}
}
</script>
arc() 方法创建弧/曲线
context.arc(x,y,r,sAngle,eAngle,counterclockwise);
x圆的中心的 x 坐标。
y圆的中心的 y 坐标。
r圆的半径。
sAngle起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
eAngle结束角,以弧度计。
counterclockwise可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。
这就是全部的代码了。
在页面上试一下,首先是随机生成的验证码,
开始输入不是4位数的验证码,点击确定后会刷新验证码。
然后是4位数,但是不正确的验证码,
当我们输入正确的验证码时,
这就是我做的验证码功能了,都是原生的js,没有用后台的知识,小伙伴有没有理解哦。
最近因为项目快要交付了,所有人都开始动员起来测试系统,因为小编参与了整个的开发流程,所以很多同事遇到bug都来问我,需要在哪里改,东西写在哪了。第一次知道改bug这么费精力,改了这个,又出来好几个新的,简直爆炸。
打死也不承认是我写的
不说了,一会经理要问我改多少bug了。
不敢跑,不敢跑
AJ-Captcha行为验证码,包含滑动拼图、文字点选两种方式,UI支持弹出和嵌入两种方式。后端提供Java实现,前端提供了php、angular、html、vue、uni-app、flutter、android、ios等代码示例。
源码地址:https://gitee.com/anji-plus/captcha
基于apache2协议,开源免费商用,但需要保留版权声明
Apache 2.0 开源协议具有以下特点:
1. 授权:该协议允许任何人自由使用、复制、修改、分发和销售被许可软件的副本。
2. 版权声明:被许可软件的副本必须包含原始版权声明和许可声明。
3. 专利授权:该协议授予了对软件相关专利的非专属授权,这意味着使用该软件的人不会受到专利侵权的指控。
4. 责任限制:被许可软件是按"原样"提供的,没有任何明示或暗示的担保和条件。使用者对软件的使用有责任承担风险。
5. 分发修改版本:使用者可以基于被许可软件创建衍生作品,并将其分发。然而,衍生作品必须遵循Apache 2.0协议,并包含相应的版权声明和许可声明。
总体而言,Apache 2.0 开源协议提供了灵活的许可方式,鼓励创新和共享。它广泛应用于许多开源软件项目,包括Apache HTTP服务器等。
提供了go、php、java(springboot、springmvc)多种版本,我们这里以springboot为例
直接运行 StartApplication.java,配置文件先不修改 ,使用默认的,我们一会儿再来看看都有些什么配置项
提供的版本有很多,我们这里以vue为例演示
本地启动执行命令,记得修改接口地址
npm install
npm run dev
启动成功后可以看到演示界面
进入内部页面后,还有前端代码集成示例,可以非常方便的集成到自己的项目中去
整体时序图
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
# 对于分布式部署的应用,我们建议应用自己实现CaptchaCacheService,比如用Redis或者memcache,
# 参考CaptchaCacheServiceRedisImpl.java
# 如果应用是单点的,也没有使用redis,那默认使用内存。
# 内存缓存只适合单节点部署的应用,否则验证码生产与验证在节点之间信息同步,导致失败。
# !!! 注意啦,如果应用有使用spring-boot-starter-data-redis,
# 请打开CaptchaCacheServiceRedisImpl.java注释。
# redis -----> SPI: 在resources目录新建META-INF.services文件夹(两层),参考当前服务resources。
# 缓存local/redis...
aj.captcha.cache-type=local
# local缓存的阈值,达到这个值,清除缓存
#aj.captcha.cache-number=1000
# local定时清除过期缓存(单位秒),设置为0代表不执行
#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
# 汉字统一使用Unicode,保证程序通过@value读取到是中文,可通过这个在线转换
# https://tool.chinaz.com/tools/unicode.aspx 中文转Unicode
# 右下角水印文字(我的水印)
aj.captcha.water-mark=我的水印
# 右下角水印字体(不配置时,默认使用文泉驿正黑)
# 由于宋体等涉及到版权,我们jar中内置了开源字体【文泉驿正黑】
# 方式一:直接配置OS层的现有的字体名称,比如:宋体
# 方式二:自定义特定字体,请将字体放到工程resources下fonts文件夹,支持ttf\ttc\otf字体
# aj.captcha.water-font=WenQuanZhengHei.ttf
# 点选文字验证码的文字字体(文泉驿正黑)
# aj.captcha.font-type=WenQuanZhengHei.ttf
# 校验滑动拼图允许误差偏移量(默认5像素)
aj.captcha.slip-offset=5
# aes加密坐标开启或者禁用(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
#点选文字个数,存在问题,暂不支持修改
#aj.captcha.click-word-count=4
aj.captcha.history-data-clear-enable=false
# 接口请求次数一分钟限制是否开启 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接口一分钟内请求数限制
aj.captcha.req-get-minute-limit=30
# check接口一分钟内请求数限制
aj.captcha.req-check-minute-limit=30
# verify接口一分钟内请求数限制(暂用不上,可后台直接调用captchaService)
#aj.captcha.req-verify-minute-limit=30
可以看到后端可以配置的选项还是很多的,可以切换缓存类型,展示底图,接口限制等等
我们尝试滑动一次,发现在整个请求过程中,前端向后端发出了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;
}
//取坐标信息
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("验证码坐标解析失败", 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);
}
分析一下接口的实现,经过了坐标解密(滑块图的坐标信息是通过加密后传输给前端),坐标对比,需要滑动的位置在允许的范围内才认为滑动位置正确
再来看看验证码生成的实现
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);
}
//设置水印
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、整体来说这个项目非常完整,并且提供了很多的终端实现(常见的基本上都支持了,html,vue,小程序终端,原生ios android等),并且后端也是非常容易集成到自己的项目,拿着demo版本修改一下即可
*请认真填写需求信息,我们会在24小时内与您取得联系。