内容简介:在传统的 Web 开发过程中,处理图形验证码很简单,只需要在后台用随机字符串生成一个图片,将验证码内容放进 Session 即可,用户提交表单时从 Session[1] 取出判断即可。但是现如今,越来越推崇 API 交互,无状态,在 Session 这一块,虽然默认配置是不支持了,但是还是有很多曲线救国的方法。在 API 开发中,我们也可以给前端签发 SessionID ,并且通过 PHP 的内置方法,来实现这一切。
前言
在传统的 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 ,虽然看着不咋地,但是他也实现了我们想要的,不过这个也不算是太好的方案,而且,还要考虑客户端字符串不够随机的情况,接下来我们改变一下方向,换成服务端签发。
基于服务端签发
刚刚的是基于客户端签发的实现,下面来提供另一种思路,但是大体上,这个是差不多的哈都。
理论原理
同样是签发 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 或者 直接用用二进制流生成位图显示都是可以的,我们只是需要可以验证,验证方法直接使用上面的即可。
看了这三种解决方案,基本都能满足我们的需求,可能还有人想到了另一种方案。提供一个 json 接口名,在后台生成图片然后保存起来,返回 url 和 sign 给前端,这样就好了,但是这样做,我们的资源并不太可控,会造成一定的资源浪费,这里我并没有考虑 这种方案。
文中所提到的一些知识都是对一些基础知识的应用,文章中的代码是写文章直接敲的,如果有排版错误或者逻辑错误,请不吝赐教。
文中所用到的 Redis 为 PHPRedis 扩展。至于验证码图片生成可以用 gregwar/captcha - Packagist 来做哟。
以上只是我个人的一些理解,如果你有更好的方案,不妨一起分享。
参考
[注1] 本文中所提到的 Session 为一种技术标准和和我们常说的通过浏览器自动传递 Cookie 交互中的 Session 有一定概念却别,这里只是自己手动实现了 SessionID的传递 ,但是始终保持 Session 的直译语义 “会话”。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Linux非交互SSH密码验证方案——sshpass
- iOS 12 人机交互指南:交互(User Interaction)
- 生活NLP云服务“玩秘”站稳人机交互2.0语音交互场景
- 表单正则验证及文件上传验证功能
- angular 实现同步验证器跨字段验证
- asyncio之子进程交互
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
大思维:集体智慧如何改变我们的世界
杰夫·摩根 / 郭莉玲、尹玮琦、徐强 / 中信出版集团股份有限公司 / 2018-8-1 / CNY 65.00
智能时代,我们如何与机器互联,利用技术来让我们变得更聪明?为什么智能技术不会自动导致智能结果呢?线上线下群体如何协作?社会、政府或管理系统如何解决复杂的问题?本书从哲学、计算机科学和生物学等领域收集见解,揭示了如何引导组织和社会充分利用人脑和数字技术进行大规模思考,从而提高整个集体的智力水平,以解决我们时代的巨大挑战。是英国社会创新之父的洞见之作,解析企业、群体、社会如何明智决策、协作进化。一起来看看 《大思维:集体智慧如何改变我们的世界》 这本书的介绍吧!