当我们填写了用户名和密码后,浏览器会在要求头中带上 Authorization 字段,并且将 base64 之后的用户名和密码发送过来。同时,PHP将会分别把用户名和密码解析到 $_SERVER[39;PHP_AUTH_USER'] 和 $_SERVER['PHP_AUTH_PW'] 中。
上述这种认证办法便是最大略的 HTTP Basic 认证,可以看出,这种办法进行验证的用户名和密码实在是相称于明文传输的,由于 base64 很随意马虎就可以反向解析出来。以是这种办法是非常不屈安的。那么有没有更繁芜一些的办法呢?
HTTP Digest既然这么写了,那肯定是有更好的办法啦,那便是 HTTP Digest 办法的 HTTP 认证。
$realm='Restrictedarea';//user=>password$users=array('admin'=>'mypass','guest'=>'guest');//指定Digest验证办法if(empty($_SERVER['PHP_AUTH_DIGEST'])||!$_COOKIE['login']){setcookie('login',1);//退出登录条件判断header('HTTP/1.1401Unauthorized');header('WWW-Authenticate:Digestrealm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');//如果用户不输入密码点了取消die('您点了取消,无法登录');}//验证用户登录信息if(!($data=http_digest_parse($_SERVER['PHP_AUTH_DIGEST']))||!isset($users[$data['username']])){die('WrongCredentials!');}//验证登录信息$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('WrongCredentials!');}//用户名密码验证成功echo'您的登任命户为:'.$data['username'];setcookie("login",2);//Authorization:Digestusername="guest",realm="Restrictedarea",nonce="5e815bcbb4eba",uri="/",response="9286ea8d0fac79d3a95fff3e442d6d79",opaque="cdce8a5c95a1427d74df7acbf41c9ce0",qop=auth,nc=00000002,cnonce="a42e137359673851"//做事器回答报文中的nonce值,加上username,password,httpmethod,httpuri利用MD5(或者做事器指定的其他算法)打算出request-digest,作为repsonse头域的值//获取登录信息functionhttp_digest_parse($txt){//echo$txt;//protectagainstmissingdata$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($matchesas$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://github.com/zhangyue0503/dev-blog/blob/master/php/202003/source/PHP%E7%9A%84HTTP%E9%AA%8C%E8%AF%81.php
参考文档: https://www.php.net/manual/zh/features.http-auth.php