一、寫在前面
以前做過好幾個站點涉及到社會化登入,主要使用的是 google,facebook。專案均採用 laravel 開發,使用官方外掛 "laravel/socialite"
就 ok 了,如果遇到 socialite 中沒有的 provider,則需前往 provider manager 倉庫安裝具體的 provider 了。
作為 web 專案,三方登入的 package 是比較成熟的,一般來說在網頁上建立一個三方登入的按鈕,跳轉到第三方平臺上登入,成功之後會跳轉到專案站點預設的 callback 路徑,在 callback 方法中做使用者在三方平臺登入之後在本站的認證操作。
近期,電商站點做 app,需要適配 app,一共要接入三個站點:facebook,google 和 paypal,本來以為 social 包就能處理,結果發現差異較大,浪費了不少的功夫,因此這裡做點小結。
二、Facebook 登入
facebook 是最容易對接的,也是直接能用 socialite 包拿到使用者資料的,app 那邊登入之後,可以拿到 access_token,並傳輸到後端,後端拿到 access_token 後直接可以拿到使用者的基本資料:
$userInfo = Socialite::driver($provider)->userFromToken($code);
$id = $userInfo->getId();
$name = $userInfo->getName();
$email = $userInfo->getEmail() ? $userInfo->getEmail() : $id;
三、google 登入
socialite 完全不能用,網上的文章也不靠譜。參考 google 官方文件 下載 google sdk 解決。
在所有工作開展之前,需要到 google 平臺上增加專案,並對專案進行設定,此處略去不表。
使用者在 google 登入之後,獲得了名叫 id_token 的東西,客戶端將 id_token 傳送到後臺伺服器,伺服器使用 id_token 獲取使用者資訊。
這個 id_token,是和 access_token 是類似的東西,有效期內可以使用多次,還可以通過瀏覽器進行測試:
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
在多次嘗試 socialite 庫解析使用者資料無效之後,通過瀏覽器測試確認了 token 所攜帶的資料內容,也確認了 socilalite 程式碼所提供的方法不能解析出使用者,不知道為何同樣是 oauth2,為何 google 對於 android 和 web 提供的方法不一致。
此處需要使用到 google 的 PHP sdk:composer require google/apiclient:"^2.0"
$client_id = config('service.google.client_id');
$client = new Google_Client(['client_id' => $client_id]);
try {
$payload = $client->verifyIdToken($code);
if ($payload) {
return $payload;
} else {
throw new UnauthorizedHttpException("the token is invalid", 40110);
}
} catch (\Throwable $exception) {
throw new UnauthorizedHttpException("error when verify token from google", 40111);
}
四、paypal 登入
socialite 完全不能用,網上的文章也不靠譜。經過反覆折騰,看了paypal 無數的 API 文件,SDK 使用說明,沒有找到對路的辦法,最終參考 paypal connect 官方文件解決,就是一個字,累。
在沒有找到 Paypal connect 文件之前,在 paypal 的各種 api 文件中都檢索不出頭緒來。每次還需要客戶端給生成一個 token,想著拿到的應該類似 facebook 或者 google 的 access_token,用 socialite 庫直接讀出來就好,萬萬沒想到,這貨卻是一個 auth_code,需要在 server 端到 paypal server 上換一個 access_token,然後才能拿到使用者資訊。
先是按照官方文件的例子,用 postman 測試了下,確認了 api,最終使用了 PayPal-PHP-SDK,不過這貨也是一個坑,官方已經設定為 readonly 了,基本上已經棄用,方法中的 api 介面都是舊的,不過請求的格式沒太大變化,因此可以繼承一下官方類來換取 access_token 和解析使用者資料,等有空了,可以自己實現並維護一個庫。
獲取 access_token:
public static function getAccessToken($params, $apiContext)
{
static $allowedParams = array('grant_type' => 1, 'code' => 1);
if (!array_key_exists('grant_type', $params)) {
$params['grant_type'] = 'authorization_code';
}
$clientId = $apiContext->getCredential()->getClientId();
$clientSecret = $apiContext->getCredential()->getClientSecret();
$json = self::executeCall(
"/v1/oauth2/token",
"POST",
http_build_query(array_intersect_key($params, $allowedParams)),
array(
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Basic ' . base64_encode($clientId . ":" . $clientSecret)
),
$apiContext
);
$data = json_decode($json);
return $data->access_token;
}
獲取使用者資訊:
public static function getUserInfo($params, $apiContext = null)
{
static $allowedParams = array('schema' => 1);
$params = is_array($params) ? $params : array();
if (!array_key_exists('schema', $params)) {
$params['schema'] = 'paypalv1.1';
}
$requestUrl = "/v1/identity/oauth2/userinfo?"
. http_build_query(array_intersect_key($params, $allowedParams));
$json = self::executeCall(
$requestUrl,
"GET",
"",
array(
'Authorization' => "Bearer " . $params['access_token'],
'Content-Type' => 'x-www-form-urlencoded'
),
$apiContext
);
$ret = new UserInfo();
$ret->fromJson($json);
return $ret;
}
順便,參考官方文件,寫了個簡單的頁面,並在頁面上放置一個跳轉按鈕,自己獲取 auth_code, 這樣再也不用依賴客戶端的同事給生 code 了,大大簡化了開發和測試流程。
五、結尾
小小的功能,寫了兩天,我也是醉了,究其原因有如下三點:
第一,是思想中過於依賴 socialite 庫,以為這貨考慮了 android 這樣的客戶端請求的情形,結果浪費了不少測試的事件。
第二,不得不吐槽 paypal 的官方文件了,可能因為 paypal 主要是做支付的,在 developer.paypal.com 中檢索內容,很難檢索到相關內容,最終還是產品的同事給了個連結?。
第三,是測試方法的簡陋,每次登入都需要依賴客戶端給 code,導致沒辦法快速的定位問題,不知道是 token 的問題還是程式碼的問題。
來源:www.guohuawei.com/archives/how-to-s...
本作品採用《CC 協議》,轉載必須註明作者和本文連結