使用 Dingo API 快速构建 RESTful API(八)—— API 认证实现(上)


概述

有些 API 接口需要用户认证之后才能返回响应,有些 API 接口则会根据用户认证与否返回不同的响应数据,Dingo 扩展包支持多个不同的认证驱动,在 Dingo API 中启用认证后,当我们尝试对请求进行认证时就会执行对应的驱动逻辑。下面是 Dingo 开箱支持的认证驱动:

  • HTTP 基本认证(Basic)
  • JSON Web Tokens(JWT)
  • OAuth 2.0(OAuth2)

其实 Laravel 框架提供的 API 认证解决方案 Passport 无非也是对 JWT 和 OAuth2 的封装,Dingo API 只是自己单独实现了一套 API 认证机制而已,除此之外,Dingo 扩展包还支持自定义认证驱动,下面我们将逐个介绍这些不同认证驱动的实现和使用。

HTTP 基本认证

注册驱动

在 Dingo API 中通过 HTTP 基本认证实现对请求的认证很简单,首先我们需要在 AuthServiceProviderboot 方法将基本认证注册到 Dingo 的认证管理器中:

use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\Basic;

public function boot()
{
    ...

    // Dingo 认证驱动注册
    $this->app->make(Auth::class)->extend('basic', function ($app) {
        return new Basic($app['auth'], 'email');
    });
}

这里我们指定 basic 驱动对应的实现类为 Dingo\Api\Auth\Provider\Basic,并且以用户邮箱字段作为认证唯一标识。

认证中间件

然后,就可以在相应的 API 路由中通过认证中间件标识该路由需要认证之后才能访问:

$api->version('v3', ['middleware' => 'api.auth'], function ($api) {
    $api->resource('tasks', \App\Http\Controllers\Api\TaskController::class);
});

这样定义的话,会让 Dingo 认证中间件作用到 v3 版本下的所有 Dingo API 接口,如果只想在指定路由上应用该中间件的话,可以这么做:

$api->version('v3', function ($api) {
    $api->resource('tasks', \App\Http\Controllers\Api\TaskController::class, [
        'middleware' => 'api.auth'
    ]);
});

当然,你还可以在控制器中定义中间件:

class TaskController extends ApiController
{
    public function __construct()
    {
        $this->middleware('api.auth');
    }

    ...

}

访问认证接口

接下来,我们就可以访问需要认证的 API 接口了,访问 tasks.index 路由对应的 URL,如果没有认证的话会返回 401 Unauthorized 响应:

Dingo 401 Unauthorized

而如果添加 Basic 认证头之后,就可以正常访问这个 API 接口了,添加 Basic 认证头可以通过在 Postman 的 Authorization 标签页中设置实现,选择认证类型为 Basic Auth,然后在右侧表单输入认证凭证(我们在设置驱动的时候指定以邮箱作为认证标识,所以这里用户名字段输入邮箱信息):

Postman Basic Authorization

发起请求,就可以正常返回响应数据了:

Dingo Http Basic Authorization

获取认证用户信息

如果你想要在应用代码中获取认证用户信息,可以这么做:

use Dingo\Api\Auth\Auth;

$user = app(Auth::class)->user();

我们可以改写 Api\TaskControllerindex 方法如下,只获取当前认证用户名下的任务信息:

public function index(Request $request)
{
    $limit = $request->input('limit') ? : 10;
    // 获取认证用户实例
    $user = app(Auth::class)->user();
    $tasks = Task::where('user_id', $user->id)->paginate($limit);
    return $this->response->paginator($tasks, new TaskTransformer());
}

在引入了 Dingo\Api\Routing\Helpers Trait 的类中,还可以通过 $this->auth->user() 获取当前认证用户。

JWT 认证

安装 JWT 扩展包

HTTP 基本认证虽然简单,但是不够安全,因为密码信息只是通过 Base64 进行编码,相当于明文传输,所以并不推荐,为此我们引入了 JSON Web Tokens,简称 JWT,关于 JWT 认证的原理可以参考 Laravel 5 中使用 JWT 实现基于 API 的用户认证这篇教程,Dingo 中基于 JWT 的认证同样也是基于里面提到的 tymon/jwt-auth 扩展包。所以在使用该认证驱动之前,先要通过 Composer 安装这个依赖:

composer require tymon/jwt-auth

安装完成后将配置文件发布到 config 目录下:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"

最后通过如下 Artisan 命令生成 JWT 的密钥用于后续生成 Token:

php artisan jwt:secret

在继续后续步骤之前,还要让用于认证的 User 模型类实现 JWTSubject 接口,以便可以通过 JWT 进行认证:

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    ...

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

注册驱动

然后我们可以在 config/api.php 中注册对应的认证驱动:

'auth' => [
    'jwt' => \Dingo\Api\Auth\Provider\JWT::class,
],

或者和 HTTP 基本认证一样,在 AuthServiceProviderboot 方法中通过如下方式注册:

use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\JWT;
use Tymon\JWTAuth\JWTAuth;

$this->app->make(Auth::class)->extend('jwt', function ($app) {
    return new JWT($app[JWTAuth::class]);
});

获取 JWT 令牌

注册认证中间件和 HTTP 基本认证一样,并没有什么区别,只是访问 API 接口的认证方式不一样而已,在通过 JWT 进行认证之前,先要获取 JWT 令牌值,我们可以编写一个 API 接口来获取:

$api->version('v3', function ($api) {
    $api->post('user/auth', function () {
        $credentials = app('request')->only('email', 'password');
        try {
            if (! $token = \Tymon\JWTAuth\Facades\JWTAuth::attempt($credentials)) {
                throw new \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException('Invalid credentials');
            }
        } catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
            throw new \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException('Create token failed');
        }

        return compact('token');
    });

    ...
});

在 Postman 中访问该路由,即可获取到 Token 值:

获取 JWT 令牌

基于 JWT 认证访问 API

有了 JWT 令牌值,就可以通过在请求头中设置该令牌值实现 JWT 认证了,其它代码保持和 HTTP 基本认证一样,我们在 Postman 中设置认证类型为 Bearer Token,并将上一步获取到的 Token 值设置到右侧的 Token 字段中:

Postman Bearer Authorization

然后访问 tasks.index 路由,就可以成功获取到该用户名下的任务列表了:

Dingo Jwt Authorization

至此,基于 JWT 认证的 API 接口就已经实现了,下一篇我们将继续介绍如何基于 OAuth 2.0 实现 Dingo API 的认证。


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: 使用 Dingo API 快速构建 RESTful API(七)—— 错误及异常处理

>> 下一篇: 使用 Dingo API 快速构建 RESTful API(九)—— API 认证实现(下)