0

0

API 交互中怎么做好图片验证码

步履不停

步履不停

发布时间:2019-06-18 16:20:03

|

3633人浏览过

|

来源于php中文网

原创

API 交互中怎么做好图片验证码

前言

在传统的 Web 开发过程中,处理图形验证码很简单,只需要在后台用随机字符串生成一个图片,将验证码内容放进 Session 即可,用户提交表单时从 Session[1] 取出判断即可。

但是现如今,越来越推崇 API 交互,无状态,在 Session 这一块,虽然默认配置是不支持了,但是还是有很多曲线救国的方法。

基于 Session 实现

在 API 开发中,我们也可以给前端签发 SessionID ,并且通过 PHP 的内置方法,来实现这一切。
比如 我们与前段约定,当在请求中包含有 X-Session-Id ,且不为空时,表示这个会话已经注册过 SessionID ,否则就颁布一个 SessionID 并返回在 Response Header 中的 X-Session-Id 让前段记录这个 SessionID ,下面简单实现一下。

// code_session.php
session_start();
// 这里假设已经通过 Header 获取到了 SessionID,并保存到了 $sessionId 变量中。
// 当 SessionID 不存在,或者 为空 则创建新的 SessionID 。
if(!isset($sessionId) || empty($sessionId)){
    $sessionId = session_create_id();
    // 因为前台还没有 SessionID ,所以下发一个,通知前端保存。
    header('X-Session-Id: '.$sessionId);
}
// 设置当前会话的 SessionID 。
session_id($sessionId);
// 这里我们就可以自由的读写 Session 了。
// 生成验证码
$code = mt_rand(1e3 ,1e4-1);
// create_image 请自行实现 或者使用现有的图形验证码库生成。
$image = create_image($code);
// 存储进去 Session
$_SESSION['code'] = $code;
// 输出一张图片
$image->output();

上面基本实现了生成图片,前端需要根据 只需要再提交表单时,在 headers 中带上 X-Session-ID 即可。

// code_session_validate.php

session_start();
// 这里假设已经通过 Header 获取到了 SessionID,并保存到了 $sessionId 变量中。
// 当 SessionID 不存在,或者 为空 则创建新的 SessionID 。
if(
  !isset($sessionId) 
|| empty($sessionId) 
|| !isset($_POST['code']) 
|| empty($_POST['code'])
){
    // 因为没有提交 SessionID 过来 这个肯定就是不成立的了,所以直接终止即可。
    exit;
}
// 设置当前会话的 SessionID 。
session_id($sessionId);
if($_POST['code']!=$_SESSION['code']){
    // 验证码错误啦
    exit;
}
// 验证通过了就删掉 code,
unset($_SESSION['code']);

上面使用 Session ,我们基本就实现了一个简单的验证,而且是基于 API 交互的,不依赖浏览器 cookie 。当我们需要一些复杂的比如共享  Session ,这些就不在本文的讨论范围了(其实现在也已经超纲了)

基于客户端主动签发

接下来的方法是无状态的,但是需要用到 Redis 。这里使用 PHPRedis 这个扩展来处理。

在大多数情况下,我们并不需要像上面使用 Session 那样来创建过多的 Session ,造成有一些资源浪费,当然,Session 可以做的不止这些,下面我们就用 Redis 来做一个客户端主动签发图片验证码

理论原理

由客户端本地生成随机字符串,然后拼接在获取验证码地址的后面,后端截取客户端生成的随机字符串,用此作为验证凭证放入 Redis 中去,再客户端提交时需要带上先前生成的随机字符串一同进项验证。

// code_client.php
$salt = 'wertyujkdbaskndasda';
if(!isset($_GET['sign'])){
    // 客户端没有提供签名,停止执行
    exit;
}
// 用户传来的一切数据都是不可靠的,我们需要对其加盐后执行 md5
$sign = md5($_GET['sign'].$salt);
// 拼接上签名作为 Redis 的 key
$key = 'code:'.$sign;
// 连接 Redis 
$cache = new \Redis();
// 生成验证码
$code = mt_rand(1e3,1e4-1);
// 保存验证码到 Redis 并设置2分钟的有效期。
if($cache->exists($key)){
    // 这个 Key 已经被占用了,这里先停止。
    exit;
}
$cache->set($key,$code,60*2);
// 创建图片并返回
$image = create_image($code);
$image->output();

好了,接下来验证一下。

// code_client_validate.php
$salt = 'wertyujkdbaskndasda';
if(
!isset($_POST['sign'])
|| !isset($_POST['code']) // 没有提交验证码过来。
|| !empty($_POST['code'])
){
    // 客户端没有提供签名,停止执行
    exit;
}
// 用户传来的一切数据都是不可靠的,我们需要对其加盐后执行 md5
$sign = md5($_POST['sign'].$salt);
// 拼接上签名作为 Redis 的 key
$key = 'code:'.$sign;
// 连接 Redis 
$cache = new \Redis();

if(!$cache->exists($key)){
    // 根本没有这个 key
    eixt;
}

if($cache->get($key)!=$_POST['code']){
    // 验证码错误
}

// 验证通过了就删除

$cache->del($key);

看着是不是要复杂点儿,甚至还用上了 Redis ,虽然看着不咋地,但是他也实现了我们想要的,不过这个也不算是太好的方案,而且,还要考虑客户端字符串不够随机的情况,接下来我们改变一下方向,换成服务端签发。

基于服务端签发

刚刚的是基于客户端签发的实现,下面来提供另一种思路,但是大体上,这个是差不多的哈都。

通义万相
通义万相

通义万相,一个不断进化的AI艺术创作大模型

下载

理论原理

同样是签发 Sign ,只不过这次由服务端来签发,然后将 Sign 通过 Header 发送给客户端,客户端需要先取到图片资源,注意这里返回的应该是一个合法的二进制流,然后从 header 中取出 Sign ,同时展示给用户。

// code_server.php
$cache = new \Redis();
$salt = 'wertyujkdbaskndasda';
function generateSign(){
    global $cache,$salt;
    $sign = md5(mt_rand().$salt);
    // 拼接上签名作为 Redis 的 key
    $key = 'code:'.$sign;
    if($cache->exists($key)){
        // 是的 你么有看错,就是如果生成的 Sign 已存在,就进行递归,直到生成出一个不存在的。
        return generateSign();
    }
    return $key;
}
// 连接 Redis 
$key = generateSign();
// 生成验证码
$code = mt_rand(1e3,1e4-1);
// 保存验证码到 Redis 并设置2分钟的有效期。
$cache->set($key,$code,60*2);
// 创建图片并返回
$image = create_image($code);
// 哈哈 要剃掉前缀哟
header('X-Captcha-Sign: ' . str_replace('code:','',$key));
$image->output();

看起来几乎没有变化,只是生成 Sign 的方式变了一下,但是,这样搞的话,前端同学可能就不爽了,他们要先获取这个资源和 headers 中的 X-Captcha-Sign 再 show 到界面上,当然 可以直接将结果 base64 或者 直接用用二进制流生成位图显示都是可以的,我们只是需要可以验证,验证方法直接使用上面的即可。

特别注意

当你使用 ajax 获取这个资源是,如果你的业务涉及到了跨域,你还需要在响应头设置 Access-Control-Expose-Headers - HTTP | MDN,否则 ajax 无法获取自定义的响应头。。

header('Access-Control-Expose-Headers: X-Captcha-Sign');

总结

看了这三种解决方案,基本都能满足我们的需求,可能还有人想到了另一种方案。提供一个 json 接口名,在后台生成图片然后保存起来,返回 url 和 sign  给前端,这样就好了,但是这样做,我们的资源并不太可控,会造成一定的资源浪费,这里我并没有考虑 这种方案。

文中所提到的一些知识都是对一些基础知识的应用,文章中的代码是写文章直接敲的,如果有排版错误或者逻辑错误,请不吝赐教。

文中所用到的 Redis 为 PHPRedis 扩展。至于验证码图片生成可以用 gregwar/captcha - Packagist 来做哟。

以上只是我个人的一些理解,如果你有更好的方案,不妨一起分享。

参考

PHP: Sessions - Manual

[注1] 本文中所提到的 Session 为一种技术标准和和我们常说的通过浏览器自动传递 Cookie 交互中的 Session 有一定概念却别,这里只是自己手动实现了 SessionID的传递 ,但是始终保持 Session 的直译语义 “会话”。

更多PHP相关技术文章,请访问PHP教程栏目进行学习!

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2691

2023.09.01

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

1663

2023.10.11

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

1526

2023.10.11

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

974

2023.10.23

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

1440

2023.10.23

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

1235

2023.11.03

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1509

2023.11.09

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1306

2023.11.13

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

8

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

微信小程序开发之API篇
微信小程序开发之API篇

共15课时 | 1.2万人学习

Laravel---API接口
Laravel---API接口

共7课时 | 0.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号