一、看守器

看守器 Guard 是一组契约,定义了一些认证和登录的常用方法。

Authenticatable trait

Illuminate\Auth\Authenticatable

Laravel 中定义的 Authenticatable trait,也是 Laravel auth 默认的 User 模型使用的 trait,这个 trait 定义了 User 模型默认认证标示符为 'id',密码字段为 'password',remember token 对应的字段为 remember_token 等等。

通过重写 User 模型的这些方法可以修改一些设置。

Guard 接口

Illuminate\Contracts\Auth\Guard

Guard 接口定义了某个实现了 Authenticatable (可认证的) 模型或类的认证方法以及一些常用的接口。

// 判断当前用户是否登录
public function check();
// 判断当前用户是否是游客(未登录)
public function guest();
// 获取当前认证的用户
public function user();
// 获取当前认证用户的 id,严格来说不一定是 id,应该是上个模型中定义的唯一的字段名
public function id();
// 根据提供的消息认证用户
public function validate(array $credentials = []);
// 设置当前用户
public function setUser(Authenticatable $user);

StatefulGuard 接口

Illuminate\Contracts\Auth\StatefulGuard

StatefulGuard 接口继承自 Guard 接口,除了 Guard 里面定义的一些基本接口外,还增加了更进一步、有状态的 Guard.

新添加的接口有这些:

// 尝试根据提供的凭证验证用户是否合法
public function attempt(array $credentials = [], $remember = false);
// 一次性登录,不记录session or cookie
public function once(array $credentials = []);
// 登录用户,通常在验证成功后记录 session 和 cookie 
public function login(Authenticatable $user, $remember = false);
// 使用用户 id 登录
public function loginUsingId($id, $remember = false);
// 使用用户 ID 登录,但是不记录 session 和 cookie
public function onceUsingId($id);
// 通过 cookie 中的 remember token 自动登录
public function viaRemember();
// 登出
public function logout();

对契约的实现

有了契约之后就要实现契约了,Laravel 框架自己针对上述契约实现了三个看守器类(guard) : RequestGuard,TokenGuard,SessionGuard.

RequestGuard
Illuminate\Auth\RequestGuard

实现了 Guard ,RequestGuard 是一个非常简单的 guard. RequestGuard 是通过传入一个闭包来认证的。可以通过调用 Auth::viaRequest 添加一个自定义的 RequestGuard.

SessionGuard
Illuminate\Auth\SessionGuard

实现了 StatefulGuard,是 Laravel web 认证默认的 guard,定义了完整的 session 方式登录实现。

TokenGuard
Illuminate\Auth\TokenGuard

实现了 Guard,适用于无状态 api 认证,通过 token 认证。但这里面实现的方法也挺少的,你可以根据这个实现一个简单的 token 认证。

Tymon\JWTAuth\JWTGuard

然后主角登场了,JWTGuard 实现了 Guard,和上面的三个实现是同级的,你可以理解为,官方的 TokenGuard 功能太简单,这个扩展写了一个比 TokenGuard 功能更加丰富的 Guard。

Gurad 的使用

我们现在已经知道 Guard 是什么一个东西已经它的实现了,那怎么使用呢?打开下面文件:
config/auth.php

// 这里是指定默认的看守器
// web 的意思取下面 guards 数组 key 为 web 的那个
// passwords 是重置密码相关,暂时不懂什么意思
'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

// 这里定义可以用的 guard
// driver 指的就是上面的对 Guard 契约的具体实现那个类了
// users 是下面 providers 数组 key 为 users 的那个
'guards' => [
    'web' => [
        'driver' => 'session',  // SessionGuard 实现
        'provider' => 'users',  
    ],

    'api' => [
        'driver' => 'jwt',  // JWTGuard 实现,源码中为 token,我这改成 jwt 了
        'provider' => 'users',
    ],
],

// 这个的作用是指定认证所需的 user 来源的数据表
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],

    // 'users' => [
    //     'driver' => 'database',
    //     'table' => 'users',
    // ],
],

通过以上你就知道了:
1.认证用的那些方法是通过实现了 Guard 契约,契约保证了框架与扩展之间的低耦合性,为什么这样可以低耦合,后面中间件和辅助函数会具体介绍
2.JWT 的 JWTGuard 实现了 Guard 契约
3.定义的 Guard 如何具体使用

二、中间件

看 JWT 的文档,里面定义的 AuthController 方法使用的是 auth:api 中间件,而 JWT 还提供了 jwt.auth 和 jwt.refresh 中间件,那么这些中间件有什么不同又是如何起作用的呢?

1. 定义

1.1 框架的中间件

app\Http\Kernel.php

这个文件中定义了框架自带的中间件:

protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

auth:api

可以发现 auth:api 使用的就是第一个中间件,而后面 :api 是路由参数,指定了要使用哪个看守器,可以看到下面 api 对应的看守器就是 jwt 的看守器。

并且你可以直接使用 auth ,这样就相当于使用 defaults 中指定的看守器,即 session。

Lumen 默认用的就是 api 那个,所以你直接用 auth 作为 api 路由的中间件完全没问题

Laravel 中指定了两个看守器,而且默认的并不是 api,所以你必须得用 auth:api 作为路由的中间件

功能是检查 token 的有效性,决定是否放行。

1.2 jwt-auth 的中间件

tymon\jwt-auth\src\Providers\AbstractServiceProvider.php

protected $middlewareAliases = [
    'jwt.auth' => Authenticate::class,
    'jwt.check' => Check::class,
    'jwt.refresh' => RefreshToken::class,
    'jwt.renew' => AuthenticateAndRenew::class,
];

jwt.auth
这个和上面的功能完全一致,至于有什么区别后面会具体解释。

jwt.refresh
这个出来检验 token 的有效性并决定如何放行外,还会在返回的 header 头上加入新的 token,达到每次请求都换取新 token 的效果。

jwt.refresh 和 jwt.auth区别
这个的区别就是前者会在响应的 header 头中增加刷新的新 token。

jwt.auth 和 auth:api(auth)
这两个功能完全一致,只是调用链有所差别,而这个差别正好可以体现上面提到的低耦合性。