学习 Lumen 用户认证 (一)

很久没写 PHP 代码了,尤为是 Lumen,我是 Lumen 的忠实用户,自从面世开始,我就将 Lumen 做为我 API 的主要框架使用。php

但说到 API,不得不说的一个概念:「先后端分离」,如今愈来愈多的团队都采用先后端分离,完全解放出前端的优点,也让后台更加集中于数据的输出。关于这方面的讨论,不在这里讨论了,能够参考一些文章深刻研究:前端

segmentfault.com/a/119000000…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 官网提供的例子:bash

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 会在系统须要认证的时候被调用,此方法接受一个匿名函数传参,在此匿名函数内,你能够任意的解析 App\User 并返回,或者在解析失败时返回 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
qrcode
相关文章
相关标签/搜索