PHP第三方登入學習筆記
一、OAuth2.0
(一)什麼是OAuth
全稱為Open Authorization,即開放式授權。
OAuth協議為使用者資源的授權提供了一個安全的、開放而又簡易的標準。與以往的授權方式不同之處是OAuth的授權不會使第三方觸及到使用者的賬戶資訊(如使用者名稱與密碼),即第三方無需使用使用者的使用者名稱與密碼就可以申請獲得該使用者資源的授權,因此OAuth是安全的。
(二)OAuth的工作原理
(三)OAuth的應用場景
- QQ使用者授權慕課網使用其QQ賬號相關的資訊
- 獲取授權後,在符合許可權規則的情況下訪問各種API
(四)三個重要步驟
步驟一:請求OAuth登入頁
Request Token URL:未授權的令牌請求服務地址
(帶有特定引數的URL,如AppID、AppKey、回撥地址)
步驟二:使用者使用第三方賬號登入並授權
第三方賬號登入成功後,會跳轉到指定的回撥地址,並帶上一個GET引數code,這個code是會過期的,有效期非常短,並且只可以使用一次。
步驟三:返回登入結果
User Authorization URL:使用者授權的令牌請求服務地址
使用者QQ登入授權之後需要請求一個帶有特定引數的URL(此時使用者已授權)
請求驗證通過之後會得到一個響應資料,從這個相應資料可以知道這個使用者的暱稱、頭像等基本賬號資訊。
(五)關於AccessToken
AccessToken:使用者通過第三方應用訪問OAuth介面的令牌
相應資料裡面包含AccessToken,有了它,第三方站點就可以以使用者的身份執行OAuth服務方允許執行的操作,如:分享QQ空間。
(六)AccessToken和Refresh Token
1、資料傳輸原理
訪問開放平臺的介面實質上都是通過第三方應用拼接一個特定的URL來訪問API的。即使是連續幾次訪問同一個API介面,動態拼接的URL也有可能是不一樣的,即任意一次訪問一個API都需要重新拼接一個URL,而且每次都需要用上AccessToken。出於安全考慮,一般都是使用POST發起HTTP請求。開放平臺再將資料打包成xml/json返回。
2、生命週期解析
AccessToken有生命週期,較長(10天半個月甚至更長)。
如果AccessToken過期了,
(1)重新授權登入第三方站點
(2)User Authorization URL中指定引數need_refresh_token=true(不同開放平臺叫法不一樣)指明返回時攜帶Refresh Token引數。當AccessToken快過期時,就可以使用Refresh Token動態拼接URL請求獲得一個新的AccessToken。
Refresh Token也有生命週期,更長(幾個月半年甚至更長)。
二、第三方登入——QQ登入
(一)前置條件
- 一個QQ號
- 一臺公網通過域名可訪問的web伺服器(沒有域名無法申請到AppID)
- 關於備案
下面表格0表示免備案,1表示必須備案:
伺服器 \ 用途 | 開發練手 | 釋出上線 | ||
---|---|---|---|---|
中國大陸 | 騰訊=0 | 0 | 1 = 1 | 騰訊=1 | 1 | 1 = 1 |
工信部=1 | 工信部=1 | |||
港澳臺及國外 | 騰訊=0 | 0 | 0 =0 | 騰訊=1 | 1 | 0 =1 |
工信部=0 | 工信部=0 |
(二)申請AppID和AppKey
QQ互聯:https://connect.qq.com/
登入,點選應用管理,填寫資料,郵箱驗證,完成註冊。完成之後就可以進去建立應用了。
建立過程中注意驗證步驟:
(三)引入官方SDK
SDK是官方提供的接入QQ登入的示例程式碼。
SDK下載:http://wiki.connect.qq.com/sdk%E4%B8%8B%E8%BD%BD
SDK引數配置:訪問SDK的install目錄,根據要求填寫表單。
請求授權列表中:get_user_info是獲取使用者基本資訊
勾選的許可權會在登入頁面中展示:
配置成功之後,只保留API目錄即可。
(四)SDK解讀
文件資料:http://wiki.connect.qq.com/
SDK核心類和重要方法
1、登入授權相關的三個主要類(API/class/*.class.php)
Recorder.class.php:配置讀取與SESSION存取
URL.class.php:基於CURL庫的get與post請求
Oauth.class.php:Oauth相關URL動態拼接與token操作
Recorder.class.php:
- __construct()
$incFileContents = file(ROOT."comm/inc.php");//讀入配置檔案
$incFileContents = $incFileContents[1];
$this->inc = json_decode($incFileContents);//解析成php物件
- readInc($name)
return $this->inc->$name;
//->readInc('appid')即讀取配置檔案的appid
URL.class.php:
- combineURL(keysArr)
$combined = $baseURL."?";//拼接?
foreach($keysArr as $key => $val){
$valueArr[] = "$key=$val";//拼接引數
}
$keyStr = implode("&",$valueArr);//使用&拼接引數鍵值對
- get(keysArr) 傳送get請求
- post(keysArr, $flag = 0) 傳送post請求
Oauth.class.php:
- qq_login() 拼接QQ登入頁面URL
$appid = $this->recorder->readInc("appid");//讀取appid
$callback = $this->recorder->readInc("callback");//讀取回調地
$scope = $this->recorder->readInc("scope");//讀取授權列表
//-------生成唯一隨機串防CSRF攻擊
$state = md5(uniqid(rand(), TRUE));//原樣返回引數
$this->recorder->write('state',$state);//state寫入session中
//-------構造請求引數列表
$keysArr = array(
"response_type" => "code",
"client_id" => $appid,
"redirect_uri" => $callback,
"state" => $state,
"scope" => $scope
);
//拼接URL之後
$login_url = $this->urlUtils->combineURL(self::GET_AUTH_CODE_URL, $keysArr);
header("Location:$login_url");//跳轉到生成的url
- qq_callback() QQ登入完成後的回撥處理
$state = $this->recorder->read("state");
//--------驗證state防止CSRF攻擊
if(!$state || $_GET['state'] != $state){//返回的state和本地的不一致丟擲錯誤提示
$this->error->showError("30001");
}
//State引數是可選的
//為資料傳輸再新增一道安全保障
//-------請求引數列表
$keysArr = array(
"grant_type" => "authorization_code",
"client_id" => $this->recorder->readInc("appid"),
"redirect_uri" => urlencode($this->recorder->readInc("callback")),
"client_secret" => $this->recorder->readInc("appkey"),
"code" => $_GET['code']
);
//------構造請求access_token的url
$token_url = $this->urlUtils->combineURL(self::GET_ACCESS_TOKEN_URL, $keysArr);
$response = $this->urlUtils->get_contents($token_url);
(五)SDK優化
- 調整檔案及目錄結構
- SDK中的常量名太常見可能和現有專案衝突,批量替換SDK中的常量名稱為不常見名稱
(六)開發
首頁index.php:
<?php
require_once 'Connect2.1/qqConnectAPI.php';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<?php if (!isset($_COOKIE['qq_accesstoken']) && !isset($_COOKIE['qq_openid'])){ ?>
<a href="qqlogin.php">QQ登入</a>
<?php }else{ ?>
<a href="qqlogout.php">退出QQ</a>
<?php
$qc=new QC($_COOKIE['qq_accesstoken'],$_COOKIE['qq_openid']);
$userinfo=$qc->get_user_info();
var_dump($userinfo);
} ?>
</body>
</html>
申請上線需要使用官網提供的QQ登入按鈕素材。
QQ登入頁面qqlogin.php:
require_once 'Connect2.1/qqConnectAPI.php';
//訪問QQ登入頁面
$oauth=new Oauth();
$oauth->qq_login();
回撥地址頁面callback.php:
require_once 'Connect2.1/qqConnectAPI.php';
//請求access token
$oauth=new Oauth();
$accessToken=$oauth->qq_callback();
//獲取openID
//QQ使用者在第三方站點的唯一標識
//同一個QQ使用者在不同站點使用QQ登入openID始終一樣
$openID=$oauth->get_openid();
setcookie('qq_accesstoken',$accessToken,time()+86400);//時間要比access token的有效期短
setcookie('qq_openid',$openID,time()+86400);
header('Location: index.php');
退出頁面qqlogout.php:
setcookie('qq_accesstoken',null);
setcookie('qq_openid',null);