PHP下的Oauth2.0尝试 - OpenID Connect

OpenID Connect

OpenID Connect简介

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

OpenID的历史是什么?

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

OIDC基础

简要而言,OIDC是一种安全机制,用于应用链接到身份认证服务器(Identity Service)获取用户信息,并将这些信息以安全可靠的方法返回给应用。
在最初,由于OpenID1/2常常和OAuth协议(一种受权协议)一块儿说起,因此两者常常被搞混。segmentfault

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)时发送过去。curl

OpenID Connect 实现

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

证书

# 生成私钥 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 \OAuth2\Storage\Pdo($pdo);

    $config = [
        'use_openid_connect' => true, //openid 必须设置
        'issuer' => 'sxx.qkl.local'
    ];

    $server = new \OAuth2\Server($storage, $config);

    // 第二个参数,必须设置值为public_key
    $server->addStorage($this->getKeyStorage(), 'public_key');

    // 添加 Authorization Code 授予类型
    $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($storage));

    // 添加 Client Credentials 授予类型  通常三方应用都是直接经过client_id & client_secret直接请求获取access_token
    $server->addGrantType(new \OAuth2\GrantType\ClientCredentials($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 \OAuth2\Storage\Memory(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 = \OAuth2\Request::createFromGlobals();
    $response = new \OAuth2\Response();

    // 验证 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();
}

clipboard.png

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

clipboard.png

总结

access_token 用于受权
id_token(一般为JWT) 用于认证

一般咱们
首先,须要使用id_token登陆
而后,你会获得一个access_token
最后,使用access_token来访问受权相关接口。
相关文章
相关标签/搜索