leezj / laravel-jwt-api
Laravel-JWT-API
Requires
- php: >=7.1
- tymon/jwt-auth: ^1.0.0
README
Laravel-JWT-API开发包
[对Laravel框架进行一些配置处理,使其在开发API时更加便捷]
背景
随着前后端完全分离,PHP也基本告别了view模板嵌套开发,转而专门写资源接口。《Laravel》是PHP框架中最优雅的框架,国内也越来越多人告别《ThinkPHP》选择了《Laravel》。Laravel框架本身对API有支持,但是在实际工作中还是需要再做一些处理。《Lumen》用起来不顺手,有些包不能很好地支持。所以,对Laravel框架进行一些配置处理,使其在开发API时更加便捷。
环境和程序要求
功能
- 统一Response响应处理
- Laravel Api-Resource资源 分页返回统一响应
- jwt-auth用户认证与无感知自动刷新
- 单一设备登录
- 异常捕获,http状态码统一
安装
- 通过composer,这是推荐的方式,可以使用composer.json声明依赖,或者直接运行下面的命令。
composer require leezj/laravel-jwt-api
- 放入composer.json文件中
"require": { "Leezj/laravel-jwt-api": "*" }
然后运行
composer update
使用
添加服务提供商
'providers' => [
...
Leezj\LaravelApi\ApiServiceProvider::class,
]
2.发布配置文件
php artisan vendor:publish --provider="Leezj\LaravelApi\ApiServiceProvider"
此命令会在config目录下生成一个leezj.php配置文件,你可以在此进行自定义配置。
response
是配置资源响应格式exception
是配置需要拦截的异常
1.统一Response响应处理
所有请求都返回json格式,返回字段格式也是统一标准化,格式如下
{
"message": "string",
"code": xxx,
"data": [] // 数组或对象
}
默认success返回的http状态码是200,error返回的状态码是400
- 新建Api控制器基类或者继承Api控制器基类
use ApiResponse;
- 代码使用
// 成功返回 第一个参数可接受item和resource return $this->success($user,$meta); // 只返回信息无内容 return $this->setHttpCode(201)->message('用户创建成功...'); // 错误返回 return $this->error('用户登录失败',401);
4.返回
// 成功完整返回
{
"message": "Success",
"code": 200,
"data": [
{
"id": 1,
"name": "jack"
},
{
"id": 2,
"name": "tony"
}
],
"meta": {
"page_info": {
"current_page": 1,
"last_page": 20,
"per_page": 15,
"total": 500
}
}
}
// 错误返回
{
"message": "Error",
"code": 401,
"data": []
}
2.Api-Resource资源 分页返回统一响应
- 在Resource资源文件中引入
use PaginatedCollection;
示例代码
namespace App\Http\Resources; use App\Traits\PaginatedCollection; use Illuminate\Http\Resources\Json\JsonResource; class Company extends JsonResource { use PaginatedCollection; }
3.使用示例
return \App\Http\Resources\Company::collection($company);
4.返回同上成功返回示例
关于分页返回的字段,你可以在配置文件中指定:
config('leezj.response.page_info')
默认是current_page、last_page、per_page、total 4个
3.异常自定义处理
1.修改app/Exceptions目录下的Handler.php文件
use LaravelApi\Response\ExceptionReport; public function render($request, Exception $exception) { // 将方法拦截到自己的ExceptionReport $reporter = ExceptionReport::make($exception); if ($reporter->shouldReturn()) { return $reporter->report(); } return parent::render($request, $exception); }
2.可自定义错误的异常设置 配置文件leezj.php,在exception.do_report加入需要拦截的异常,示例:
'exception' => [
'do_report'=>[
UnauthorizedHttpException::class => [
'msg' => '未授权或Token签名失效', // message显示的消息
'http_code' => 401 // http状态码,不填则获取异常类的状态码,如果获取不到则500
],
AuthenticationException::class => [
'msg' => '未授权或Token签名失效',
'http_code' => 401 // 响应体中code代码,可用于业务标识
]
]
]
4.jwt-auth
1.打开config目录下的app.php文件,添加服务提供者
'providers' => [
...
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]
2.发布配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
此命令会在config目录下生成一个jwt.php配置文件,你可以在此进行自定义配置。
3.生成密钥
php artisan jwt:secret
此命令会在你的.env文件中新增一行JWT_SECRET=secret。以此来作为加密时使用的秘钥。
4.配置Auth guard. 打开config目录下的auth.php文件,修改api的驱动为jwt。这样,我们就能让api的用户认证变成使用jwt。
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
5.更改User Model 如果需要使用jwt-auth作为用户认证,我们需要对我们的User模型进行一点小小的改变,实现一个接口,变更后的User模型如下
class User extends Authenticatable implements JWTSubject { ... public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; } }
5.自动刷新用户认证 && 单一设备登录
为了保证安全性,用户的token都会定期自动刷新为全新的,用旧的token请求不会通过。当刷新了token,能否不要重新登录,就算重新登录也是一周甚至一个月之后呢?给用户一种无感知的体验。
1.增加中间件别名 打开app/Http目录下的Kernel.php文件,添加如下一行
protected $routeMiddleware = [
......
'api.refresh'=>\Leezj\LaravelApi\Middleware\RefreshTokenMiddleware::class,
];
2.路由器修改
Route::middleware('api.refresh:api')->group(function () {
// api看守器登陆授权
...
});
3.登录控制器引入LoginActionTrait
class LoginController extends Controller
{
use LoginActionTrait;
...
}
4.原理
- 自动刷新用户认证
- 捕获到了token过期所抛出的TokenExpiredException异常
- 刷新用户的token $token = $this->auth->refresh();
- 使用一次性登录以保证此次请求的成功 注意需要设置config jwt宽限时间
Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
- 在响应头中返回新的token $this->setAuthenticationHeader($next($request), $token);
- 单一设备登录
- 我们将token都存到缓存中。在登录接口,获取到last_token里的值,将其加入黑名单。
- 这样,只要我们无论在哪里登录,之前的token一定会被拉黑失效,必须重新登录,我们的目的也就达到了。