使用 Dingo API 扩展包快速构建 Laravel RESTful API(九)—— API 认证实现(下)

上篇教程中,我们介绍了如何通过 HTTP 基本认证和 JWT 认证实现 Dingo API 的认证,这篇教程,学院君将会给大家介绍如何在 Dingo API 中基于 OAuth 2.0 和自定义认证驱动实现 API 认证。

OAuth 2.0 认证

Laravel Passport 一样,Dingo API 的 OAuth 2.0 认证基于第三方扩展包 league/oauth2-server 实现,该扩展包还提供了针对 Laravel 框架的适配包 lucadegasperi/oauth2-server-laravel,因此,在 Dingo API 中实现 OAuth 2 认证的话,直接安装配置后面这个扩展包即可。但是从 Laravel 5.3 开始,由于 Laravel 官方提供的 API 认证扩展包 Passport 也是基于 league/oauth2-server 实现的,所以后面这个适配包就废弃了,不仅如此,Dingo API 后续的版本也移除了 OAuth 2 驱动(Dingo\Api\Auth\Provider\OAuth2),所以,如果你是在 Dingo API 包含 OAuth2 且 Laravel 框架版本低于 5.3 的老版本中实现 OAuth 2 认证,可以使用 lucadegasperi/oauth2-server-laravel 这个适配包快速实现,否则需要在 Dingo API 中自定义 OAuth2 驱动并自行配置 league/oauth2-server 实现 OAuth 2 认证,或者,你可以直接基于 Passport 实现 Dingo 的认证,包括上篇教程提到的 JWT 认证,也可以基于 Passport 实现(Passport 使用了另一个 JWT 扩展包实现基于 JWT 的 API 认证)。

在这篇教程中,为了简化流程,我们将直接基于 Passport 实现 Dingo API 的 OAuth2 认证(5.3 之前老版本实现可以参考 Dingo 文档),关于 OAuth2 的底层原理,可以参考 Passport 文档中的介绍,这里我们将重点放到认证实现上。

安装配置 Passport

如果你还没有在项目中安装并初始化 Passport 扩展包的话,可以参考 Passport 官方文档进行安装配置。这里由于我们基于待办任务项目进行演示,在之前的教程中已经初始化过,所以可以跳过这一步骤。

最后,记得将 config/auth.php 中的 guards.api.driver 配置值调整为 passport 以便在应用中生效:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

设置认证中间件

基于 Passport 认证的话,就需要修改之前设置的认证中间件了,打开 Api\TaskController.php,将定义在构造函数中的认证中间件调整为 auth:api

public function __construct()
{
    $this->middleware('auth:api');
}

这样一来,在认证的时候就会通过 Laravel 框架提供的中间件来校验了,此外,还需要修改获取认证用户的代码:

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

通过密码授权令牌访问认证 API

OAuth 2 提供了多种获取授权令牌的方法,比如通过授权码颁发访问令牌、密码授权令牌、隐式授权令牌、私人访问令牌等,具体实现可以参考 Passport 官方文档或者学院君写的系列教程,这里我们以密码授权令牌为例做演示。

首先,我们通过如下 Artisan 命令创建一个新的需要接入 API 认证的客户端应用:

php artisan passport:client --password

Passport Client

这样一来,我们就获取到对应的 APP ID 和 APP Secret,将其配置到 .env 中:

CLIENT_ID=7
CLIENT_SECRET=7lz6yKdRWudXgwtct6esjwEjk8DpjjFs10lMkvFh

然后我们在 routes/api.php 中定义一个用于获取授权令牌的路由:

$api->version('v3', function ($api) {
    ...
    $api->post('user/token', function () {
        app('request')->validate([
            'email' => 'required|string',
            'password' => 'required|string',
        ]);

        $http = new \GuzzleHttp\Client();
        // 发送相关字段到后端应用获取授权令牌
        $response = $http->post(route('passport.token'), [
            'form_params' => [
                'grant_type' => 'password',
                'client_id' => env('CLIENT_ID'),
                'client_secret' => env('CLIENT_SECRET'),
                'username' => app('request')->input('email'),  // 这里传递的是邮箱
                'password' => app('request')->input('password'), // 传递密码信息
                'scope' => '*'
            ],
        ]);

        return response()->json($response->getBody()->getContents());
    });
    $api->resource('tasks', \App\Http\Controllers\Api\TaskController::class);
});

当我们在 Postman 中模拟访问 user/token 时,就可以获取到对应的访问令牌了:

通过 Passport 密码授权获取访问令牌

其中 access_token 就是后续访问认证 API 时需要的令牌。还是在 Postman 中,我们将 access_token 字段值设置到类型为 Bearer 的 Authorization 字段,然后请求 tasks.index 路由,如果能返回相应的响应数据,表示认证成功:

Dingo 基于 Passport 实现 OAuth2 认证

至此,在 Dingo API 中基于 Passport 实现 OAuth2 认证就演示到这里,其它类型的 OAuth2 认证请参考学院君上篇提到的教程自行去实践。

自定义认证驱动

如果以上介绍的 HTTP 基本认证、JWT 认证以及 OAuth2 认证驱动都不能满足你的需求,你还可以选择在 Dingo 中自定义认证驱动。自定义认证驱动类需要实现 Dingo\Api\Contract\Auth\Provider 接口,如果认证成功,则返回对应的认证用户实例,否则要抛出 Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException 异常:

use Illuminate\Http\Request;
use Dingo\Api\Routing\Route;
use Dingo\Api\Contract\Auth\Provider;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class CustomProvider implements Provider
{
    public function authenticate(Request $request, Route $route)
    {
        // Logic to authenticate the request.

        throw new UnauthorizedHttpException('Unable to authenticate with supplied username and password.');
    }
}

如果你需要使用从 Authorization 请求头中获取到的令牌,和 Dingo 默认提供的 BasicJWT 驱动一样,可以选择继承 Dingo\Api\Auth\Provider\Authorization 基类,该基类也实现了 Dingo\Api\Contract\Auth\Provider 接口,只是新增了从 Authorization 请求头获取字段值的逻辑,我们在自定义驱动中只需要实现 getAuthorizationMethod 方法来返回 Authorization 请求头的认证类型即可:

use Illuminate\Http\Request;
use Dingo\Api\Routing\Route;
use Dingo\Api\Auth\Provider\Authorization;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class CustomProvider extends Authorization
{
    public function authenticate(Request $request, Route $route)
    {
        $this->validateAuthorizationHeader($request);

        // If the authorization header passed validation we can continue to authenticate.
        // If authentication then fails we must throw the UnauthorizedHttpException.
    }

    public function getAuthorizationMethod()
    {
        return 'mac';
    }
}

定义完自定义驱动类就可以在配置文件 config/api.php 中配置通过自定义驱动实现 Dingo API 的认证:

'auth' => [
    'custom' => 'CustomProvider',
],

或者在服务提供者的 boot 方法中扩展 Dingo 认证管理器驱动数组以便在应用中可以通过自定义驱动进行 API 认证:

app(\Dingo\Api\Auth\Auth::class)->extend('custom', function ($app) {
    return new CustomProvider;
});

关于 Dingo API 的认证实现学院君就介绍到这里,下一篇教程我们将开始介绍 API 接口访问频率限制的实现。

学院君 has written 1197 articles

Laravel学院院长,终身学习者

积分:157472 等级:P12 职业:手艺人 城市:杭州

0 条回复

登录后才能进行评论,立即登录?