• PHP下的Oauth2.0尝试


    OpenID Connect

    OpenID Connect简介

    OpenID Connect是基于OAuth 2.0规范族的可互操作的身份验证协议。它使用简单的REST / JSON消息流来实现,和之前任何一种身份认证协议相比,开发者可以轻松集成。
    OpenID Connect允许开发者验证跨网站和应用的用户,而无需拥有和管理密码文件。OpenID Connect允许所有类型的客户,包括基于浏览器的JavaScript和本机移动应用程序,启动登录流动和接收可验证断言对登录用户的身份。

    OpenID的历史是什么?

    OpenID Connect是OpenID的第三代技术。首先是原始的OpenID,它不是商业应用,但让行业领导者思考什么是可能的。OpenID 2.0设计更为完善,提供良好的安全性保证。然而,其自身存在一些设计上的局限性,最致命的是其中依赖方必须是网页,但不能是本机应用程序;此外它还要依赖XML,这些都会导致一些应用问题。
    OpenID Connect的目标是让更多的开发者使用,并扩大其使用范围。幸运的是,这个目标并不遥远,现在有很好的商业和开源库来帮助实现身份验证机制。

    OIDC基础

    简要而言,OIDC是一种安全机制,用于应用连接到身份认证服务器(Identity Service)获取用户信息,并将这些信息以安全可靠的方法返回给应用。
    在最初,因为OpenID1/2经常和OAuth协议(一种授权协议)一起提及,所以二者经常被搞混。

    OpenID是Authentication,即认证,对用户的身份进行认证,判断其身份是否有效,也就是让网站知道“你是你所声称的那个用户”;
    OAuth是Authorization,即授权,在已知用户身份合法的情况下,经用户授权来允许某些操作,也就是让网站知道“你能被允许做那些事情”。
    由此可知,授权要在认证之后进行,只有确定用户身份只有才能授权。

    (身份验证)+ OAuth 2.0 = OpenID Connect

    OpenID Connect是“认证”和“授权”的结合,因为其基于OAuth协议,所以OpenID-Connect协议中也包含了client_id、client_secret还有redirect_uri等字段标识。这些信息被保存在“身份认证服务器”,以确保特定的客户端收到的信息只来自于合法的应用平台。这样做是目的是为了防止client_id泄露而造成的恶意网站发起的OIDC流程。

    在OAuth中,这些授权被称为scope。OpenID-Connect也有自己特殊的scope--openid ,它必须在第一次请求“身份鉴别服务器”(Identity Provider,简称IDP)时发送过去。

    OpenID Connect 实现

    我们的本代码实现建立在PHP下的Oauth2.0尝试 - 授权码授权(Authorization Code Grant) 文章的代码基础上调整

    证书

    
    # 生成私钥 private key
    $ openssl genrsa -out privkey.pem 2048
    
    # 私钥生成公钥 public key
    $ openssl rsa -in privkey.pem -pubout -out pubkey.pem
    

    调整server

    
    private function server()
    {
        $pdo = new PDO('mysql:host=ip;dbname=oauth_test', "user", "123456");
        $storage = new OAuth2StoragePdo($pdo);
    
        $config = [
            'use_openid_connect' => true, //openid 必须设置
            'issuer' => 'sxx.qkl.local'
        ];
    
        $server = new OAuth2Server($storage, $config);
    
        // 第二个参数,必须设置值为public_key
        $server->addStorage($this->getKeyStorage(), 'public_key');
    
        // 添加 Authorization Code 授予类型
        $server->addGrantType(new OAuth2GrantTypeAuthorizationCode($storage));
    
        // 添加 Client Credentials 授予类型  一般三方应用都是直接通过client_id & client_secret直接请求获取access_token
        $server->addGrantType(new OAuth2GrantTypeClientCredentials($storage));
    
        return $server;
    }
    
    private function getKeyStorage()
    {
        $rootCache = dirname(APP_PATH) . "/cert/oauth/";
        $publicKey  = file_get_contents($rootCache.'pubkey.pem');
        $privateKey = file_get_contents($rootCache.'privkey.pem');
    
        // create storage
        $keyStorage = new OAuth2StorageMemory(array('keys' => array(
            'public_key'  => $publicKey,
            'private_key' => $privateKey,
        )));
    
        return $keyStorage;
    }
    

    授权

    
    public function authorize()
    {
        // scope增加openid
        // 该页面请求地址类似:
        // http://sxx.qkl.local/v2/oauth/authorize?response_type=code&client_id=testclient&state=xyz&redirect_uri=http://sxx.qkl.local/v2/oauth/cb&scope=basic%20get_user_info%20upload_pic%20openid
        //获取server对象
        $server = $this->server();
        $request = OAuth2Request::createFromGlobals();
        $response = new OAuth2Response();
    
        // 验证 authorize request
        // 这里会验证client_id,redirect_uri等参数和client是否有scope
        if (!$server->validateAuthorizeRequest($request, $response)) {
            $response->send();
            die;
        }
    
        // 显示授权登录页面
        if (empty($_POST)) {
            //获取client类型的storage
            //不过这里我们在server里设置了storage,其实都是一样的storage->pdo.mysql
            $pdo = $server->getStorage('client');
            //获取oauth_clients表的对应的client应用的数据
            $clientInfo = $pdo->getClientDetails($request->query('client_id'));
            $this->assign('clientInfo', $clientInfo);
            $this->display('authorize');
            die();
        }
    
        $is_authorized = true;
        // 当然这部分常规是基于自己现有的帐号系统验证
        if (!$uid = $this->checkLogin($request)) {
            $is_authorized = false;
        }
    
        // 这里是授权获取code,并拼接Location地址返回相应
        // Location的地址类似:http://sxx.qkl.local/v2/oauth/cb?code=69d78ea06b5ee41acbb9dfb90500823c8ac0241d&state=xyz
        $server->handleAuthorizeRequest($request, $response, $is_authorized, $uid);
        if ($is_authorized) {
            // 这里会创建Location跳转,你可以直接获取相关的跳转url,用于debug
            $parts = parse_url($response->getHttpHeader('Location'));
            var_dump($parts);
            parse_str($parts['query'], $query);
    
            // 拉取oauth_authorization_codes记录的信息,包含id_token
            $code = $server->getStorage('authorization_code')
                ->getAuthorizationCode($query['code']);
            var_dump($code);
        }
    //        $response->send();
    }
    

    curl获取

    
    # 使用 HTTP Basic Authentication
    $ curl -u testclient:123456 http://sxx.qkl.local/v2/oauth/token -d 'grant_type=client_credentials'
    
    # 使用 POST Body 请求
    $ curl http://sxx.qkl.local/v2/oauth/token -d 'grant_type=client_credentials&client_id=testclient&client_secret=123456'
    

    postman获取access_token

    总结

    
    access_token 用于授权
    id_token(通常为JWT) 用于认证
    
    通常我们
    首先,需要使用id_token登录
    然后,你会得到一个access_token
    最后,使用access_token来访问授权相关接口。
    

    原文地址:https://segmentfault.com/a/1190000016539242

  • 相关阅读:
    真爱 vs. 种姓:新一代印度人的婚恋观
    美国司法部解禁guns打印技术
    特朗普访英,吃瓜群众却只想看《真爱至上》
    Semaphore(信号量)
    RLock(递归锁)
    用python编写九九乘法表
    php传值和传引用的区别
    post请求的header
    Content-type详解
    thinkphp5 学习笔记
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9969846.html
Copyright © 2020-2023  润新知