2

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

但说到 API,不得不说的一个概念:「前后端分离」,现在越来越多的团队都采用前后端分离,彻底解放出前端的优势,也让后台更加集中于数据的输出。关于这方面的讨论,不在这里讨论了,可以参考一些文章深入研究:

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

正因为有了前后端分离,后台关注于接口 API 的输出,当时 Lumen 的出现,就是为 RESTful API 而生的:

Decidedly Laravel. Delightfully Minimal.

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

将 Lumen 作为接口框架使用,不得不解决一个核心问题:如何对访问者进行「认证」。

用户认证

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


Coding01
831 声望123 粉丝