Laravel Socialite 詳解

不久以前 Dearmadman 曾寫過一篇 使用 Laravel Socialite 集成微信登陸 的文章,可是彷佛仍是有些同窗不太明白,詢問着如何集成 QQ 登陸,那麼,本篇咱們就來剖析一下 Laravel Socialite 的詳細內容。讓各位同窗都得到 Laravel Socialite 所提供的驅動適配能力。php

Laravel Socialite

Laravel Socialite 爲第三方應用的 OAuth 認證提供了很是豐富友好的接口,咱們使用它能夠很是方便快捷的對相似微信、微博等第三方登陸進行集成。git

那麼首先,你須要知道 Laravel Socialite 主要是用於那些 OAuth 受權登陸的。這些場景下的第三方應用好比微信,QQ,微博等,都使用了 OAuth 協議爲用戶提供受權其它應用獲取用戶信息及權限的能力。github

Open Authorization

OAuth(開放受權)是一個開放標準,容許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。json

OAuth 容許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每個令牌受權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的 2 小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth 讓用戶能夠受權第三方網站訪問他們存儲在另外服務提供者的某些特定信息,而非全部內容。數組

咱們的應用與使用 OAuth 標準的第三方應用的交互流程通常是這樣的:微信

  • 展現跳轉到第三方應用登陸的連接app

  • 用戶點擊連接跳轉到第三方應用登陸並進行受權ide

  • 在用戶受權後第三方應用會跳轉到咱們所指定的應用回調資源地址並伴隨用於交互 AccessToken 的 Code函數

  • 咱們的應用拿到 Code 後自主請求第三方應用並使用 Code 獲取該用戶的 AccessTokenjsonp

  • 獲取 AccessToken 以後的應用便可自主的從第三方應用中獲取用戶的資源信息

詳解

對於以上流程,咱們來分析一下 Laravel Socialite 針對上面的每一步都作了些什麼,咱們將構建一個 QQProvider 來做爲演示示例:

第三方應用的登陸連接

顯而易見的,第一步咱們須要構建用於跳轉到第三方登陸的 URL,這裏須要提供一些額外的參數來進行應用的識別,好比,當你爲你的應用註冊 QQ 登陸網站接入應用時,QQ 互聯會爲你提供一組數據用來驗證登陸來源的合法性,一般這些關鍵數據爲如下幾項:

{
  appid: 'xxxxxxxxx',
  app_secret: 'xxxxxxxxx',
  redirect: 'xxxxxxxxxxx'
}

在 Laravel Socialite 中這些關鍵數據映射到了 AbstractProvider 頂級抽象中,你能夠將這些配置項定義到 services.php 配置文件中:

'qq' => [
    'client_id' => 'your-qq-app-id',
    'client_secret' => 'your-qq-app-secret',
    'redirect' => 'http://your-callback-url',
],

咱們將會在後續註冊擴展驅動時使用這些配置。

若是須要爲第三方應用登陸構建自定義的驅動,那麼你須要先繼承 AbstractProvider,而且實現 ProviderInterface 接口,由於國內流行的應用都使用的 OAuth 2.0 協議,好比 QQ,就是使用的這種協議,因此咱們須要繼承 LaravelSocialiteTwoAbstractProvider 抽象類,而且實現 ProviderInterface 接口。

ProviderInterface 接口限定了兩個方法: redirectuser,其中 redirect 方法就是返回構建後的跳轉響應,用於跳轉到第三方應用進行受權登陸,而 user 方法就是用來在用戶贊成受權登陸以後,獲取第三方應用中用戶的信息數據的。這些方法已經在 AbstractProvider 抽象類中提供了實現。因此基本上你不須要再去實現它。

Socialite 經過三個函數的配合來構造跳轉 URL,它們分別是 getAuthUrlbuildAuthUrlFromBasegetCodeFields

  • getAuthUrl($state): 返回構造完成後的跳轉 URL,它須要調用 BuildAuthUrlFromBase 函數,咱們只須要在這裏指定第三方應用所規定的應用登陸受權的地址便可。

  • BuildAuthUrlFromBase($url, $state): 它主要是爲受權地址構建相應的查詢參數,將驅動配置中的關鍵數據以及第三方應用所規定的數據填充到 URL 中。在它的內部須要調用 getCodeFields 函數,咱們只須要在這裏返回第三方應用所規定的應用登陸受權的地址與查詢參數合併便可。

  • getCodeFields($state = null): 將驅動配置信息映射到第三方應用登陸驗證所需的參數中。

使用 Code 換取 AccessToken

當用戶贊成登陸受權以後,第三方應用會將地址跳轉到 redirect 所配置的地址中,並攜帶 code 參數,你須要使用 code 來換取 access_token。而後使用 access_token 換取用戶的信息數據。這就是集成登陸的整個流程。

AbstractProvider 中已經爲 user 方法提供了基本的實現,而且它將常常變化的部分提取到了其它的方法中,這樣咱們在開發新驅動的時候,基本不須要重寫 user 方法。咱們來看一些爲了完成這些操做,咱們須要用到哪些方法。

首先,在登陸回調的路由中,咱們會得到請求中攜帶的 code 參數,而後須要使用 code 來換取 access_token,咱們先來看一下底層 AbstractProvideruser 的實現:

/**
 * {@inheritdoc}
 */
public function user()
{
    if ($this->hasInvalidState()) {
        throw new InvalidStateException;
    }

    $response = $this->getAccessTokenResponse($this->getCode());

    $user = $this->mapUserToObject($this->getUserByToken(
        $token = Arr::get($response, 'access_token')
    ));

    return $user->setToken($token)
                ->setRefreshToken(Arr::get($response, 'refresh_token'))
                ->setExpiresIn(Arr::get($response, 'expires_in'));
}

其中用到的關鍵方法有:

  • getAccessTokenResponse($code): 它須要使用 code 來換取 access_token 響應的主體,它應該返回一個包含了 access_token 的數組。根據不一樣應用所規定的響應格式,你可能會須要在這裏作出一些適配,那麼,在集成自定義驅動時,重寫這個方法就能夠了。

  • getUserByToken($token): 使用 access_token 向第三方應用獲取用戶的數據。它應該返回一個數組。

  • mapUserToObject(array $user): 它應該在使用 access_token 換取用戶數據以後,將用戶數據數組存儲到 LaravelSocialiteTwoUser 實例中的 user 屬性中,並將相應的數據映射到實例的屬性。

因此流程上也就很清晰了,那麼擴展自定義的驅動也就很簡單了,調用 getAccessTokenResponse 方法時,須要指定換取 token 的 URL,因此咱們須要重寫 getTokenUrl 方法:

/**
 * {@inheritdoc}.
 */
public function getTokenUrl()
{
    return 'https://graph.qq.com/oauth2.0/token';
}

在構建換取 token 的 URL 時,還要組合相應的參數,因此咱們須要重寫 getTokenFields 方法:

/**
* {@inheritdoc}.
*/
protected function getTokenFields($code)
{
    return [
        'client_id' => $this->clientId, 'client_secret' => $this->clientSecret,
        'code' => $code, 'grant_type' => 'authorization_code',
        'redirect_uri' => $this->redirectUrl,
    ];
}

而後,在 getUserByToken 方法中完成使用 token 換取用戶數據的業務邏輯就能夠了,它應該返回一個數組:

/**
 * {@inheritdoc}.
 */
public function getUserByToken($token)
{
    $response = $this->getHttpClient()->get('https://graph.qq.com/oauth2.0/me', [
        'query' => [
            'access_token' => $token,
        ],
    ]);

    $openId = json_decode($this->jsonpToJson($response->getBody()->getContents()), true)['openid'];
    $this->setOpenId($openId);

    $response = $this->getHttpClient()->get('https://graph.qq.com/user/get_user_info', [
        'query' => [
            'oauth_consumer_key' => $this->clientId, 'access_token' => $token,
            'openid' => $openId, 'format' => 'json'
        ]
    ]);


    return json_decode($response->getBody(), true);

}

最後就是註冊了,在 使用 Laravel Socialite 集成微信登陸 中咱們已經談到過 Socialite 自定義驅動的實現機制:

<?php

namespace Crowdfunding\Providers\Socialite;

use Illuminate\Support\ServiceProvider;

class WechatServiceProvider extends ServiceProvider
{
    public function boot()
    {
       $this->app->make('Laravel\Socialite\Contracts\Factory')->extend('wechat', function ($app) {
            $config = $app['config']['services.wechat'];
            return new WechatProvider(
                $app['request'], $config['client_id'],
                $config['client_secret'], $config['redirect']
            );
       });
    }
    public function register()
    {

    }
}

Laravel Socialite 對 OAuth 協議的流程進行了高度的抽象,並將相應的功能解耦,因此在自定義擴展時你並不須要作出太多,若是你想要了解的更多,你能夠參考 larastarscn/socialite 的源碼,它只是簡單的爲微信,QQ,微博的登陸提供了擴展驅動。

PS: 歡迎關注簡書 Laravel 專題,也歡迎 Laravel 相關文章的投稿 :),做者知識技能水平有限,若是你有更好的設計方案歡迎討論交流,若是有錯誤的地方也請批評指正,在此表示感謝謝謝 :)

相關文章
相關標籤/搜索