Laravel JWT认证详解,中间件自定义检测 JWT,附项目仓库代码

前言

本文主要是详细介绍了如何在 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不可再用。


附图

项目目录截图
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/CharmHeart/article/details/112470329