PHP的HTTP验证
在日常开发中,我们进行用户登录的时候,大部分情况下都会使用 session 来保存用户登录信息,并以此为依据判断用户是否已登录。但其实 HTTP 也提供了这种登录验证机制,我们今天就来学习关于 HTTP 验证相关的知识。
HTTP Basic
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} else {
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
// Authorization: Basic YWFhOmFhYQ==
echo base64_decode('YWFhOmFhYQ==');
// aaa:aaa 等于明文
还是直接就从代码入手,上面的代码就是最简单的一种 HTTP 认证方式,如果 $_SERVER['PHP_AUTH_USER'] 不存在,那么我们就向浏览器发送一个 401 响应头,就是告诉浏览器我们需要登录验证。当浏览器收到这个响应头时,就会弹出一个浏览器自带的验证框并要求输入用户名和密码。
当我们填写了用户名和密码后,浏览器会在请求头中带上 Authorization 字段,并且将 base64 之后的用户名和密码发送过来。同时,PHP将会分别把用户名和密码解析到 \$_SERVER['PHP_AUTH_USER'] 和 $_SERVER['PHP_AUTH_PW'] 中。
上述这种认证方式就是最简单的 HTTP Basic 认证,可以看出,这种方式进行验证的用户名和密码其实是相当于明文传输的,因为 base64 很容易就可以反向解析出来。所以这种方式是非常不安全的。那么有没有更复杂一些的方式呢?
HTTP Digest
既然这么写了,那肯定是有更好的方式啦,那就是 HTTP Digest 方式的 HTTP 认证。
$realm = 'Restricted area';
//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');
// 指定 Digest 验证方式
if (empty($_SERVER['PHP_AUTH_DIGEST']) || !$_COOKIE['login']) {
setcookie('login', 1); // 退出登录条件判断
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="' . $realm .
'",qop="auth",nonce="' . uniqid() . '",opaque="' . md5($realm) . '"');
// 如果用户不输入密码点了取消
die('您点了取消,无法登录');
}
// 验证用户登录信息
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']])) {
die('Wrong Credentials!');
}
// 验证登录信息
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
$valid_response = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
// $data['response'] 是浏览器客户端的加密内容
if ($data['response'] != $valid_response) {
die('Wrong Credentials!');
}
// 用户名密码验证成功
echo '您的登录用户为: ' . $data['username'];
setcookie("login", 2);
// Authorization: Digest username="guest", realm="Restricted area", nonce="5e815bcbb4eba", uri="/", response="9286ea8d0fac79d3a95fff3e442d6d79", opaque="cdce8a5c95a1427d74df7acbf41c9ce0", qop=auth, nc=00000002, cnonce="a42e137359673851"
// 服务器回复报文中的nonce值,加上username,password, http method, http uri利用MD5(或者服务器指定的其他算法)计算出request-digest,作为repsonse头域的值
// 获取登录信息
function http_digest_parse($txt)
{
// echo $txt;
// protect against missing data
$needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
$data = array();
$keys = implode('|', array_keys($needed_parts));
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? false : $data;
}
if($_GET['logout']){
setcookie("login", 0);
header("Location: /");
}
从代码量就可以看出这种方式复杂了很多。首先是我们一样需要在未登录的情况下返回 401 响应头,告诉浏览器我们要进行 Digest 认证。这里 header 信息就有不一样的地方了,格式是 Digest ,内容也比 Basic 多了许多,这些多出来的内容都是我们在验证认证内容的时候需要用到的。
接着,浏览器一样是会弹出输入用户名和密码的弹窗。然后将加密后的用户名和密码信息提交上来。我们可以看到返回值里有明文的 username ,但是没有明文的密码了。其实密码是通过 username 、 密码 、 nonce 、 nc 、 cnoce 、cop 、$_SERVER['REQUEST_METHOD'] 、 uri 等这些内容进行 md5 加密后生成的,放在了 response 字段中提交了上来。我们也需要按照同样的规则获得加密后的密码进行比对就可以判定用户名和密码正确从而让用户完成正常的登录流程。
在这段代码中,我们加入了一个 cookie ,是为了做退出登录的判断使用的。因为 HTTP 认证这种形式的过期时间是以浏览器为基准的。也就是如果客户端关闭了浏览器,则客户端浏览器内存中保存的用户名和密码才会消失。这种情况下我们只能通过 cookie 来进行退出登录的操作,如果用户退出登录了就改变这个 cookie 的内容并重新发送 401 响应头给浏览要求重新登录。
总结
HTTP 验证的这种操作一般不会做为我们日常开发中的正常登录功能,大部分情况下,我们会给后台或者一些特殊的管理工具加上一层这种 HTTP 认证来实现双重的认证,也就是为了保障后台的数据安全。比如,我会在我的 phpMyAdmin 上增加一层这个认证。另外,HTTP 认证也可以直接在 Nginx 或 Apache 中直接配置,不需要走到 PHP 这一层来,这个我们将来学习 Nginx 的时候会再做说明。
参考文档:
https://www.php.net/manual/zh/features.http-auth.php
PHP的HTTP验证的更多相关文章
- 【探索】无形验证码 —— PoW 算力验证
先来思考一个问题:如何写一个能消耗对方时间的程序? 消耗时间还不简单,休眠一下就可以了: Sleep(1000) 这确实消耗了时间,但并没有消耗 CPU.如果对方开了变速齿轮,这瞬间就能完成. 不过要 ...
- C# 中参数验证方式的演变
一般在写方法的时候,第一步就是进行参数验证,这也体现了编码者的细心和缜密,但是在很多时候这个过程很枯燥和乏味,比如在拿到一个API设计文档的时候,通常会规定类型参数是否允许为空,如果是字符可能有长度限 ...
- Yii1.1的验证规则
在Yii1.1的数据验证是由CValidator完成,在CValidator中提供了各种基本的验证规则 <?php public static $builtInValidators=array( ...
- 【WCF】使用“用户名/密码”验证的合理方法
我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...
- PHP验证用户登录例子-学习笔记
1.基本流程: 2.UML类图: 3.PHP代码: 3.1 index.php <?php /** * Created by PhpStorm. * User: andy * Date: 16- ...
- 客户端的验证插件validator
简单,智能,令人愉悦的表单验证~~~ 官方文档:http://www.niceue.com/validator/ <!DOCTYPE html> <html> <head ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(66)-MVC WebApi 用户验证 (2)
系列目录 前言: 回顾上一节,我们利用webapi简单的登录并进行了同域访问与跨域访问来获得Token,您可以跳转到上一节下载代码来一起动手. 继续上一篇的文章,我们接下来演示利用拿到的Token来访 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(65)-MVC WebApi 用户验证 (1)
系列目录 前言: WebAPI主要开放数据给手机APP,其他需要得知数据的系统,或者软件应用,所以移动端与系统的数据源往往是相通的. Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能, ...
- 再谈C#采集,一个绕过高强度安全验证的采集方案?方案很Low,慎入
说起采集,其实我是个外行,以前拔过阿里巴巴的客户数据,在我博客的文章:C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子) 中,介绍过采集用的工具,其实很Low的,分析 ...
- jQuery学习之路(8)- 表单验证插件-Validation
▓▓▓▓▓▓ 大致介绍 jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求.该插件捆绑了一套有用的验证方法,包括 ...
随机推荐
- 【笔记】numpy.array的常用基本运算以及对数据的操作
numpy.array的基本运算以及对数据的操作 设置一个问题,例如 这种只需要基本的运算就可以实现 类似的 numpy对向量的运算进行了优化,速度是相当快的,这种被称为universal funct ...
- 嵌入式Linux可用的防火墙——iptables:实现ip白名单、mac地址白名单
iptables是linux系统下的一个功能强大的模块,不仅可以用作防火墙,还可以实现NAT等众多路由功能.iptables的容器有很清晰的层次关系: 1. iptables是表的容器,iptable ...
- VLAN-3 Hybrid接口应用
一.实验拓扑图 二.实验编址 三.实验步骤 1.给对应的PC设置对应的IP和掩码还有接口,以及根据需要划分不同的vlan区域,再用文本标记出不同部门. 2.启动设备(全选) 3.首先用ping命令检查 ...
- Linux搭建SQL server服务器
我们知道在Linux下安装服务有很多方式,最为简单的也就是yum安装,但是很多服务通过yum是无法安装的,如果想使用yum安装,需要指定yum安装仓库,我们今天需要安装MSQL Server,所以需要 ...
- mysql事务的4大特性
事务的四大特性(简称ACID) 1.原子性(Atomicity) 事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做. 2.一致性(Consistency) 事务执行的结果必须是使数据 ...
- MySQL 数据库、数据表、数据的基本操作
1.数据库(database)管理 1.1 create 创建数据库 create database firstDB; 1.2 show 查看所有数据库 mysql> show database ...
- COM笔记-包容与聚合
COM不支持实现继承的原因在于这种继承方式将使得一个对象的实现同另外一个对象的实现紧紧地关联起来.在这种情况下,当基类的实现被修改后,派生类将无法正常运行而必须被修改.这就是为什么一些用C++编写大型 ...
- hive简单数据分析
简单查询分析 select brand_id from user_log limit 10; -- 查看日志前10数据 好像也没啥,和SQL相同,,limit取前多少条 , as取别名 查询条数统计 ...
- Android中Context解析
Context概念 当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context. Context是一个抽象基类,我们通过它访问当前包的资源(getResources.getA ...
- Bing每日壁纸的RESTful接口实现
0x00 存在意义 权且当作Docker打包的练习. 显然可以通过构造请求获得每天的壁纸,但是如果想要优雅地在其它地方使用这一网络资源,封装一个RESTful API将会保证整洁美观,在编写CSS等场 ...