使用 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 基本认证实现对请求的认证很简单,首先我们需要在 AuthServiceProvider
的 boot
方法将基本认证注册到 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 响应:
而如果添加 Basic 认证头之后,就可以正常访问这个 API 接口了,添加 Basic 认证头可以通过在 Postman 的 Authorization 标签页中设置实现,选择认证类型为 Basic Auth
,然后在右侧表单输入认证凭证(我们在设置驱动的时候指定以邮箱作为认证标识,所以这里用户名字段输入邮箱信息):
发起请求,就可以正常返回响应数据了:
获取认证用户信息
如果你想要在应用代码中获取认证用户信息,可以这么做:
use Dingo\Api\Auth\Auth;
$user = app(Auth::class)->user();
我们可以改写 Api\TaskController
的 index
方法如下,只获取当前认证用户名下的任务信息:
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 基本认证一样,在 AuthServiceProvider
的 boot
方法中通过如下方式注册:
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 认证访问 API
有了 JWT 令牌值,就可以通过在请求头中设置该令牌值实现 JWT 认证了,其它代码保持和 HTTP 基本认证一样,我们在 Postman 中设置认证类型为 Bearer Token,并将上一步获取到的 Token 值设置到右侧的 Token 字段中:
然后访问 tasks.index
路由,就可以成功获取到该用户名下的任务列表了:
至此,基于 JWT 认证的 API 接口就已经实现了,下一篇我们将继续介绍如何基于 OAuth 2.0 实现 Dingo API 的认证。
1 Comment
刷新令牌怎么处理?写一个中间件吗?