Laravel 使用 Json Web Token(JWT)

编辑于 2023-04-02 11:07:21 阅读 932

关于 JWT 之前写过

php - Json Web Token(JWT)的使用

go - gin 使用 Json Web Token(JWT)

今天总结下 Laravel 中 JWT 的使用

安装

composer require tymon/jwt-auth

#发布配置
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

#生成密钥,这将更新您的.env文件,例如JWT_SECRET=foobar
php artisan jwt:secret

快速开始

更新你的 User model

首先,您需要在User model上实现Tymon\JWTAuth\Contracts\JWTSubject合同,这要求您实现2种方法getJWTIdentifier()getJWTCustomClaims()

下面的示例应该能让您了解这会是什么样子。显然,你应该根据需要做任何改变,以满足你自己的需求。

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();//默认取的是主键id:$this->id
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        //一些附加信息,不要太敏感,因为这些信息是可以从token里解析出来的,即使不知道签名
        return ['name'=>$this->name, 'email'=>$this->email];
    }
}

配置看守器

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

添加一些基本的身份验证路由

Route::group(['middleware' => 'api', 'prefix' => 'auth/jwt'], function () {
    Route::post('login', [AuthJWTController::class, 'login']);
    Route::post('logout', [AuthJWTController::class, 'logout']);
    Route::post('refresh', [AuthJWTController::class, 'refresh']);
    Route::get('me', [AuthJWTController::class, 'me']);
});

控制器

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth()->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth()->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()->factory()->getTTL() * 60
        ]);
    }
}

您现在应该能够使用一些有效的凭据POST到登录端点(例如http://example.dev/auth/jwt/login),并看到这样的响应:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
    "token_type": "bearer",
    "expires_in": 3600
}

已知问题

jwt本身不依赖缓存(注销功能依赖缓存)

  • jwt不能互踢 连续创建两个token,a,b:

a没发起请求

b发起请求,然后注销,a依然可用

解决办法:

既然支持注销token,那我把之前生成过的token都注销,只保留最新的一个,这样不就实现了互踢?

想法是可以的,只是有一个小问题:没有token生成记录?那就在生成token后记录一下

注销功能分析

jti 是 JWT 的一个唯一标识符,主要用来作为一次性 token,从而回避重放(replay)攻击。jti 的值区分大小写。此声明可选。

token中包含jti参数,注销的时候会吧jti添加到缓存中(黑名单),并设置到期时间(即token到期时间);下次再拿这个token来请求,系统会先查黑名单,如果存在就提示授权未通过

参考

在线解析JWT token

https://jwt-auth.readthedocs.io/en/develop/

https://github.com/tymondesigns/jwt-auth

广而告之,我的新作品《语音助手》上架Google Play了,欢迎下载体验