學習 Lumen 用戶認證 (一)

很久沒寫 PHP 代碼了,尤爲是 Lumen,我是 Lumen 的忠實用戶,自從面世開始,我就將 Lumen 做爲我 API 的主要框架使用。php

但說到 API,不得不說的一個概念:「先後端分離」,如今愈來愈多的團隊都採用先後端分離,完全解放出前端的優點,也讓後臺更加集中於數據的輸出。關於這方面的討論,不在這裏討論了,能夠參考一些文章深刻研究:前端

https://segmentfault.com/a/11...web

正由於有了先後端分離,後臺關注於接口 API 的輸出,當時 Lumen 的出現,就是爲 RESTful API 而生的:數據庫

Decidedly Laravel. Delightfully Minimal.segmentfault

Lightning fast micro-services and APIs delivered with the elegance you expect.後端

將 Lumen 做爲接口框架使用,不得不解決一個核心問題:如何對訪問者進行「認證」。api

用戶認證

Lumen 雖然與 Laravel 使用了相同的底層類庫實現,可是因 Lumen 面向的是無狀態 API 的開發,不支持 session,因此默認的配置不一樣。Lumen 必須使用無狀態的機制來實現,如 API 令牌(Token)。安全

咱們看看 Lumen 官網提供的例子:服務器

use Illuminate\Http\Request;

$app->get('/post/{id}', ['middleware' => 'auth', function (Request $request, $id) {
    $user = Auth::user();

    $user = $request->user();

    //
}]);

其中使用了中間件:'middleware' => 'auth',咱們看看 auth 中間件函數:網絡

$app->routeMiddleware([
     'auth' => App\Http\Middleware\Authenticate::class,
 ]);

關聯的是 Authenticate 類,咱們看 Authenticate 的 handle 函數:

/**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if ($this->auth->guard($guard)->guest()) {
            return response('Unauthorized.', 401);
        }

        return $next($request);
    }

首先會判斷$this->auth->guard($guard)->guest()。咱們繼續跟進代碼到 AuthManager 中:

/**
     * Attempt to get the guard from the local cache.
     *
     * @param  string  $name
     * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
     */
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return isset($this->guards[$name])
                    ? $this->guards[$name]
                    : $this->guards[$name] = $this->resolve($name);
    }

默認傳入的 $name = null,因此咱們看看 $this->getDefaultDriver()

/**
     * Get the default authentication driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['auth.defaults.guard'];
    }

這就到了默認的配置 config 中了:

從 Lumen 源代碼中能夠看出 Lumen 的默認認證方式「api」。

咱們再來看看 Laravel 的默認認證方式:

Laravel 默認採用「web」方式,而 web 方式是使用 session 來進行用戶認證。這也就很好的說明了 Lumen 的無狀態性。

接着咱們須要明白 Lumen 如何經過「api」來進行用戶認證的。

AuthServiceProvider 存放在 app/Providers 文件夾中,此文件中只有一個 Auth::viaRequest 調用。viaRequest 會在系統須要認證的時候被調用,此方法接受一個匿名函數傳參,在此匿名函數內,你能夠任意的解析 AppUser 並返回,或者在解析失敗時返回 null,如:

/**
     * Boot the authentication services for the application.
     *
     * @return void
     */
    public function boot()
    {
        // Here you may define how you wish users to be authenticated for your Lumen
        // application. The callback which receives the incoming request instance
        // should return either a User instance or null. You're free to obtain
        // the User instance via an API token or any other method necessary.

        $this->app['auth']->viaRequest('api', function ($request) {
            if ($request->input('api_token')) {
                return User::where('api_token', $request->input('api_token'))->first();
            }
        });
    }

咱們來看看 viaRequest 函數:

/**
     * Register a new callback based request guard.
     *
     * @param  string  $driver
     * @param  callable  $callback
     * @return $this
     */
    public function viaRequest($driver, callable $callback)
    {
        return $this->extend($driver, function () use ($callback) {
            $guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider());

            $this->app->refresh('request', $guard, 'setRequest');

            return $guard;
        });
    }

這裏關鍵的是 RequestGuard,這個類的核心函數:

/**
     * Get the currently authenticated user.
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user()
    {
        // If we've already retrieved the user for the current request we can just
        // return it back immediately. We do not want to fetch the user data on
        // every call to this method because that would be tremendously slow.
        if (! is_null($this->user)) {
            return $this->user;
        }

        return $this->user = call_user_func(
            $this->callback, $this->request, $this->getProvider()
        );
    }

這個是判斷是否獲取用戶信息,主要是調用callback 函數,而這個函數就是咱們從 viaRequest 傳入的:

function ($request) {
    if ($request->input('api_token')) {
                return User::where('api_token', $request->input('api_token'))->first();
            }
        }

而這只是舉一個驗證用戶的例子,判斷請求是否傳入 api_token參數,並經過 User Model 直接匹配查找獲取 User or null。

固然在實際開發中,咱們不能只是簡單的獲取 api_token直接關聯數據庫查找用戶信息。

在 API 開發中,用戶認證是核心,是數據是否有保障的前提,目前主要有兩種經常使用方式進行用戶認證: JWT 和 OAuth2。

下一步

當前只是對 Lumen 的「用戶認證」進行簡單的瞭解,下一步經過對 「JWT」的學習,來看看如何利用 JWT 來有效的用戶認證,更加安全的保障接口信息被有效的用戶訪問。

附:
Json web token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON 的開放標準 (RFC 7519)。該 token 被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT 的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該 token 也可直接被用於認證,也可被加密。

「未完待續」


coding01 期待您繼續關注

qrcode

相關文章
相關標籤/搜索