前言
本文主要是详细介绍了如何在 laravel 中添加 jwt 头部验证,同时自定义了中间件,配置了异常类,从而丰富了报错的信息。
如果只是想简单的添加jwt,官网文档也有示例。
同时本文提供了git代码仓库,以供参考。
仓库地址:https://gitee.com/jiangfeng111/Start-Template
文末附有项目结构截图以及接口截图。
一、引入组件
引入
# 建议使用1.0以上版本
composer require tymon/jwt-auth 1.*@rc
添加解析
# 这条命令会在 config 下增加一个 jwt.php 的配置文件
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
生成密钥
# 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
php artisan jwt:secret
注册两个 Facade
这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利。
config/app.php
'aliases' => [
...
// 添加以下两行
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],
修改 auth.php
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt', // 原来是 token 改成jwt
'provider' => 'users',
],
]
更新你的模型
如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
protected $table = 'user';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'username',
'password',
];
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 [];
}
}
二、使用步骤
1.创建验证控制器
代码如下(示例):
<?php
namespace App\Api\Controllers\V1\User;
use App\Api\Controllers\Controller;
class AuthController extends Controller
{
public function login()
{
$credentials = request(['username', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return $this->error('1',[],'用户名或密码错误');
}
return $this->success('0',$this->respondWithToken($token),'登录成功');
}
public function me()
{
return response()->json(auth('api')->user());
}
public function logout()
{
auth('api')->logout();
return $this->success('0',[],'退出成功');
}
/**
* Refresh a token.
* 刷新token,如果开启黑名单,以前的token便会失效。
* 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->success('0',$this->respondWithToken(auth('api')->refresh()),'刷新成功');
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
}
注:我这里控制器的返回格式是自定义的,在Controller.php里面有定义
public function success($code,$data,$message){
return response()->json(['code'=>$code,'data'=>$data,'message'=>$message]);
}
public function error($code,$data,$message){
return response()->json(['code'=>$code,'data'=>$data,'message'=>$message]);
}
2.创建中间件
再Http / Middleware 下新建 ApiAuth文件,代码如下
<?php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class ApiAuth
{
public function handle($request, Closure $next)
{
try {
if (! $user = JWTAuth::parseToken()->authenticate()) {
//获取到用户数据,并赋值给$user
return response()->json([
'code' => 1004,
'message' => '无此用户'
], 404);
}
return $next($request);
} catch (TokenExpiredException $e) {
return response()->json([
'code' => 1003,
'message' => 'token 过期' ,
]);
} catch (TokenInvalidException $e) {
return response()->json([
'code' => 1002,
'message' => 'token 无效',
]);
} catch (JWTException $e) {
return response()->json([
'code' => 1001,
'message' => '缺少token' ,
]);
}
}
}
注册刚刚建立的中间件
Http / Kerner.php 在 $routeMiddleware [ ]中添加
'api.auth' => \App\Http\Middleware\ApiAuth::class,
3.配置异常类
Exceptions / Handler.php
public function render($request, Throwable $exception)
{
if($exception instanceof ValidationException){
return response()->json(['error' => '参数错误'], 400);
}
if($exception instanceof TokenBlacklistedException){
return response()->json(['error' => 'token 失效'], 400);
}
if($exception instanceof JWTException){
return response()->json(['error' => '缺少 token'], 400);
}
return parent::render($request, $exception);
}
注意:本文是基于laravel 8 ,故在异常类的写法时,与其他版本不一样,其他版本需要将Throwable 换成 Exception。
4.配置路由
routes / api.php
//用户验证
Route::group([
'prefix' => 'auth',
'middleware'=>'api.auth',
], function () {
Route::post('logout', [\App\Api\Controllers\V1\User\AuthController::class,'logout']);
Route::post('me', [\App\Api\Controllers\V1\User\AuthController::class,'me']);
});
Route::group(['prefix' => 'auth',], function () {
Route::post('refresh', [\App\Api\Controllers\V1\User\AuthController::class,'refresh']);
Route::post('register', [\App\Api\Controllers\V1\User\UserController::class,'register']);
Route::post('login', [\App\Api\Controllers\V1\User\AuthController::class,'login']);
});
//其他接口
Route::group([
'prefix' => 'user',
'middleware'=>'api.auth',
], function () {
Route::post('test', [\App\Api\Controllers\V1\User\UserController::class,'test']);
});
注意:
1.这里的中间件为上文所自定义的api.auth。
2.这里之所以将刷新写在中间件之外,是为了使得token过期后,还能访问一次刷新操作,刷新成功后,原有的过期token不可再用。
附图
项目目录截图