美文网首页PHP经验分享
PHP basic digest API接口鉴权

PHP basic digest API接口鉴权

作者: 程大哥T_T | 来源:发表于2019-07-12 17:56 被阅读21次

    关于basic认证和digest认证的初步理解

    初代的是basic的认证,比较容易被破解。升级版的就是加上摘要basic digest。可用于api接口请求的一个过滤,为api的安全提供一定的保护

    需要注意的地方

    有个问题是前端ajax会发送一个预请求OPTION,后端需要对此作出正确的回应前端ajax才会真正的请求。

    if($_SERVER['REQUEST_METHOD']=='OPTION'){
    header('HTTP/1.1 200');
    }

    后端代码

            if($_SERVER['REQUEST_METHOD']=='OPTION'){
                header('HTTP/1.1 200');
            }//需要正确的回应前端预请求,否则前端不会发出真正的请求
            $realm = 'abc';            //密钥
            $username = 'add';          //帐号
            $passowrd = '123123';       //密码
            $exit_user= $this->dao->select('*')->from('api_user')->where('restid')->eq($username)->andWhere('status')->eq(1)->fetch();//查找这个用户,判断是否存在和是否启用
            if (!$exit_user) {//不存在该账号
                Response::error('', 'fail');
            }
            $realm = $exit_user->realm;
            $passowrd = $exit_user->restkey;
            if (empty($_SERVER['PHP_AUTH_DIGEST']) && false) {//判断头部信息是否有添加
                header('HTTP/1.1 401 Unauthorization Required');    //401 此头弹出登录窗口
                header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth", nonce="' . uniqid() . '", opaque="' . md5($realm) . '"');
                die('您取消了本次登录,若重新登录,请刷新此页面。');
            } else {
    
                //使用函数http_digest_parse解析验证信息
                if (!($data = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || $data['username'] != $username) {
    //                var_dump($data);
                    header("HTTP/1.1 401 Unauthorization Required");
                    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth", nonce="'.uniqid().'", opaque="'.md5($realm).'"');//IE 8 需要重新发送,不然不弹窗
                    die('账号不一致错误!');
                }
    
                //拼接字符串
                $A1 = md5($username . ':' . $realm . ':' . $passowrd);
                $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
                $valid_response = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
    
                if ($data['response'] != $valid_response) {
                    header("HTTP/1.1 401 Unauthorization Required");
                    header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth", nonce="' . uniqid() . '", opaque="' . md5($realm) . '"');
                    die('账号/密码错误!加密后的字符串和前端提交上来的不一样,肯定有某个地方两端没有统一');
                }
    
                echo 'Hi ' . $username . ',恭喜你登录成功!';
            }
    
    
    
    
    // 解析字符串方法
        function http_digest_parse($txt)
        {
            $needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
            $data = array();
    
            preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $txt, $matches, PREG_SET_ORDER);
            foreach ($matches as $m) {
                $data[$m[1]] = $m[3];
                $data['uri'] = $_SERVER['REQUEST_URI'];
                unset($needed_parts[$m[1]]);
            }
            return $data;
        }
    

    前端代码

    //basic digest 的头部摘要   type需要对应当前提交的类型 , POST ,PUT , DELETE , GET
    function ajax(type,data={}) {
    let url = 'http://your_uri/'
    let path = 'api/index.php?m=web&f=authSql'   //你的请求地址
    
    let bodyurl = "/api/index.php?m=web&f=authSql";
    var userstr = encrypt('yourusername', 'yourpassword', bodyurl, type,'yourrealm');   //  yourname 和yourpassword,yourrealm是需要和后端统一才行。后端用一个表专门存这些数据
    return new Promise((resolve) =>{
    $.ajax({
    method: type,
    url: `${url}${path}`,
    data:data,
    dataType :'json',
    beforeSend: function(xhr) {
    xhr.setRequestHeader("Authorization",userstr);  //把加密后的字符串添加到头部信息中
    },
    success:(res) =>{
    resolve(res)
    }
    });
    })
    
    }
    
    
    
    //加密形成摘要的规则也需要和前端统一
    function encrypt( userName, password,requestBody,method,yourrealm) {
    
        var usermd = hex_md5(userName + ":'"+yourrealm+"':" + password);
    
        var rqmd = hex_md5(method+":"+requestBody);
        var mainmd = hex_md5(usermd+":1:1:1:auth:"+rqmd);
        var head ='Digest username="'+userName+'", realm="'+yourrealm+'", nonce="1", uri="'+requestBody+'", qop=auth, nc=1, cnonce="1", response="'+mainmd+'", opaque="1"'
        return head;
    }
    

    相关文章

      网友评论

        本文标题:PHP basic digest API接口鉴权

        本文链接:https://www.haomeiwen.com/subject/nzjqcctx.html