Laravel + Laravel-echo + EasyWeChat 實現微信掃碼登入

田勇發表於2019-03-19

掃碼登入成為一種日趨流行的登入方式,它具有較高的安全性,同時又使我們從記憶大量的賬號密碼並手動輸入的繁瑣流程中解脫出來,有些平臺甚至無賬號也能掃碼登入,連註冊的麻煩都省了。

對於接入微信開放平臺的公眾號應用來說,實現掃碼登入是相當容易的,有 EasyWeChat SDK 加持,再按著官方的文件一把梭,很快就能完成。
然而本文所要討論的是另一種情況,有時候出於某些原因,自己的公眾號不能接入開放平臺,但又想進行微信掃碼登入,這種情況下顯示就不能再換官方的套路來了。但只要我們稍作變通,就能實現這一需求。

基本思路:

  1. 登入頁顯示微信二維碼(使用 EasyWeChat SDK 建立,短時效的臨時二維碼)
  2. 使用者掃碼後推送訊息到伺服器介面,介面中根據業務情況進行判斷處理,符合條件時觸發 WechatScanLogin 事件
  3. WechatScanLogin 事件實現 ShouldBroadcast 介面,所以當它被觸發時也會向指定的頻道進行廣播
  4. 前端 laravel-echo 監聽頻道中使用者掃碼登入的訊息並進行處理

以下就來介紹一下具體實現,先放效果圖。

screenshot

具體實現

配合本文,我建立了一個簡單的示例專案,有興趣的可以克隆下來,配合原始碼一起服用,效果更佳。專案地址:https://github.com/tianyong90/laravel-qrcode-login

  1. 首先當然是建立 Laravel 專案,同時安裝前後端依賴
  • 前端最主要依賴是 laravel-echosocket.io-client

前端監聽事件廣播是關鍵,我們需要一個 websocket 服務端,Laravel 官方文件在介紹訊息廣播時提到了 Pusher 和 laravel-echo-server。因為我使用 laradock 作為開發環境,其中內建了 laravel-echo-server 容器,十分方便,所以決定直接用它。實際上也可以使用 Pusher 服務,那麼則需要安裝 pusher.js 替代 socket.io-client,同時在 .env 中修改相關配置

  1. 配置專案

主要是配置資料庫和 redis 連線,然後把 BROADCAST_DRIVER 設定為 redis(這一點很重要,如果使用 pusher 則需要修改為 pusher)

如果 QUEUE_CONNECTION 設定為 redis 了,則需要記得啟動佇列 worker.

  1. 啟動 laravel-echo-server

因為使用 laradock,所以只需要啟動時帶上 laravel-echo-server 引數就可以了,進入 laradock 目錄

docker-compose up -d nginx php-worker nginx mysql redis laravel-echo-server
  1. 建立 WechatScanLogin 事件
php artisan make:event WechatScanLogin
class WechatScanLogin implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $token;

    /**
     * Create a new event instance.
     *
     * @param $token
     */
    public function __construct($token)
    {
        $this->token = $token;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel(‘scan-login’);
    }
}

上面最關鍵的就是事件要實現 ShouldBroadcast 介面並在 broadcastOn 方法中指定要廣播的頻道。WechatScanLogin 的公開屬性 token 會自動包含在廣播資料中。

  1. 對接微信訊息伺服器

laravel-wechat 的相關配置和對接,請閱讀 EasyWeChat SDK 官方文件。

接收掃碼的訊息並進行相關處理。

public function serve()
{
    $app = app('wechat.official_account');

    $app->server->push(function ($message) {
        if ($message['Event'] === 'SCAN') {
            $openid = $message['FromUserName'];

            $user = User::where('openid', $openid)->first();

            if ($user) {
                // TODO: 這裡根據情況加入其它鑑權邏輯

                // 使用 laravel-passport 的個人訪問令牌
                $token = $user->createToken($user->name)->accessToken;

                // 廣播掃碼登入的訊息,以便前端處理
                event(new WechatScanLogin($token));

                \Log::info('haha login');
                return '登入成功!';
            }

            return '失敗鳥';
        } else {
            // TODO: 使用者不存在時,可以直接回返登入失敗,也可以建立新的使用者並登入該使用者再返回
            return '登入失敗';
        }
    }, \EasyWeChat\Kernel\Messages\Message::EVENT);

    return $app->server->serve();
}
  1. 使用 EasyWeChat 建立臨時二維碼並在頁面中顯示。
public function index()
{
    $wechat = app('wechat.official_account');

    $result = $wechat->qrcode->temporary('foo', 600);
    $qrcodeUrl = $wechat->qrcode->url($result['ticket']);

    return view('index', compact('qrcodeUrl'));
}
<img src={{ qrcodeUrl }} />>
  1. 前端使用 laravel-echo 訂閱對應的微信掃碼登入事件,接收其中的 token 並存入本地儲存作為判斷是否登入的憑據,同時這個 token 也將作為訪問後端 api 的授權依據。注意前面的程式碼中,使用了 laravel-passport 生成這個個人訪問令牌,如果不瞭解這部分原理,請查閱 Laravel 官方文件。
import Echo from 'laravel-echo'
import io from 'socket.io-client'

window.io = io

let EchoInstance = new Echo({
  broadcaster: 'socket.io',
  host: window.location.hostname + ':6001',
})

EchoInstance.channel('scan-login').listen('WechatScanLogin', e => {
    localStorage.setItem('my_token', this.token)

    // 其它處理
  })

總結

至此,簡單的掃碼登入就完成了。當然,本文示例程式碼不怎麼優雅、流程可能也有不完善的地方,主要是為了提供一個大致思路。有了這個思路,我們可以實現其它諸如掃碼簽到、掃碼投票等各種功能,具體如何就看大家的創意了。

最後放上個人部落格地址:https://tianyong90.com/, 歡迎各位大佬批評指正。

相關文章