OpenID Connect是基於OAuth 2.0規範族的可互操做的身份驗證協議。它使用簡單的REST / JSON消息流來實現,和以前任何一種身份認證協議相比,開發者能夠輕鬆集成。
OpenID Connect容許開發者驗證跨網站和應用的用戶,而無需擁有和管理密碼文件。OpenID Connect容許全部類型的客戶,包括基於瀏覽器的JavaScript和本機移動應用程序,啓動登陸流動和接收可驗證斷言對登陸用戶的身份。mysql
OpenID Connect是OpenID的第三代技術。首先是原始的OpenID,它不是商業應用,但讓行業領導者思考什麼是可能的。OpenID 2.0設計更爲完善,提供良好的安全性保證。然而,其自身存在一些設計上的侷限性,最致命的是其中依賴方必須是網頁,但不能是本機應用程序;此外它還要依賴XML,這些都會致使一些應用問題。
OpenID Connect的目標是讓更多的開發者使用,並擴大其使用範圍。幸運的是,這個目標並不遙遠,如今有很好的商業和開源庫來幫助實現身份驗證機制。sql
簡要而言,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
咱們的本代碼實現創建在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
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(); }
# 使用 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'
access_token 用於受權 id_token(一般爲JWT) 用於認證 一般咱們 首先,須要使用id_token登陸 而後,你會獲得一個access_token 最後,使用access_token來訪問受權相關接口。