如何在laravel框架中使用jwt

2023/06/09 laravel

一、什么是jwt?

JWT(JSON Web Token)是一种用于身份验证的开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。JWT通常用于身份验证和授权,它可以在客户端和服务器之间安全地传输用户声明信息,以便于身份验证和授权。

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。头部通常包含加密算法和令牌类型等信息,载荷包含用户声明信息,例如用户ID、角色、权限等信息,签名用于验证令牌的完整性和真实性。

JWT的工作流程通常如下:

1. 用户使用用户名和密码进行身份验证。
2. 服务器验证用户的凭据,并生成一个JWT。
3. 服务器将JWT作为响应发送给客户端。
4. 客户端将JWT保存在本地,并在每个请求中将其发送到服务器。
5. 服务器使用签名验证JWT的完整性和真实性,并解析出用户声明信息。
6. 服务器根据用户声明信息进行身份验证和授权。

JWT的优点包括:

1. 无状态:JWT在服务端不需要存储会话信息,因此可以轻松地扩展到多台服务器。
2. 自包含:JWT包含了所有必要的信息,因此可以减少服务器的请求次数。
3. 可扩展:JWT可以包含任意数量的声明信息,例如用户ID、角色、权限等信息。

需要注意的是,JWT虽然能够提供身份验证和授权功能,但它并不是万能的解决方案,仍然需要在实现时考虑安全性问题,例如使用合适的加密算法、设置合适的过期时间等。

好了下面开始学习在 laravel 框架中使用jwt

二、快速上手到跑路

0、环境要求:

laravel8+

PHP7.3+

1、安装jwt包

认证,获取 token,刷新 token 等功能,tymon/jwt-auth 这个包,人家都给你封装好了,直接装上去用就行了。

composer require tymon/jwt-auth

2、在 config/app.php 文件 providers数组中 添加服务提供商,

后面第三步需要用它来生成 jwt 配置文件

 'providers' =>[
     ...
     Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
 ];

3、生成配置文件 jwt.php

执行以下命令生成 jwt 的配置文件:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

4、生成jwt签名密钥,生成的密钥会放在.env配置文件中

生成 token 的时候需要用到该密钥生成签名。

php artisan jwt:secret

This will update your .env file with something like JWT_SECRET=foobar

5、更新用户模型(user.php)app/models

首先,您需要在User模型上实现Tymon\JWTAuth\Contracts\JWTSubject契约,这需要实现getJWTIdentifier()和getJWTCustomClaims()这两个方法。

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
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;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * 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 [];
    }
}

6、配置你的身份守护

在config/auth.php文件中,您需要进行一些更改,以配置Laravel使用jwt保护来为应用程序身份验证。 就是说后面的授权认证就是按照你下面配置的走了,如 jwt 认证。

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

...

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

在这里,我们告诉api卫士使用jwt驱动程序,并将api卫士设置为默认值。 我们现在可以使用Laravel的内置Auth系统,由jwt-Auth在幕后完成工作

7、添加基本的身份路由

首先,让我们在routes/api.php中添加一些路由,如下所示:

Route::prefix('auth')->group(function () {
    Route::post('login', [AuthController::class, 'login']);
    Route::post('logout', [AuthController::class, 'logout']);
    Route::post('refresh', [AuthController::class, 'refresh']);
    Route::post('me', [AuthController::class, 'me']);

});

8、创建认证控制器 AuthController

你可以手动或运行artisan命令创建AuthController:

php artisan make:controller AuthController

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

pCEYMi6.png

不好意思让你失望了,jwt库下面没有users表,别说表了,库也没有呀,那接下来我们开始新建库和表

pCEYGsH.png

生成用户账号表(我这里使用laravel默认的用户表,你当然可以使用自己新建的。)

php artisan migrate

pCEYteA.png

然后进入tinker

php artisan tinker

执行以下命令生成测试数据

>>> namespace App\Models;
> User::create(['name' => 'Test','email' =>'php_fangting@126.com','password' => bcrypt('123456')]);

pCEYDSS.png

然后使用账号登陆获取令牌

pCEY2oq.png

刷新token

pCEYhWT.png

报错了,因为我们是api接口,返回这种html对前端不太友好!应该返回json

所以我们需要更新一下 app/Exceptions/Handler.php 中的 render

/**
     * @param $request
     * @param Throwable $e
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
     * @throws Throwable
     *
     */
    public function render($request, Throwable $e)
    {
        // 参数验证错误的异常,我们需要返回 400 的 http code 和一句错误信息
        if ($e instanceof ValidationException) {
            return response(['error' => array_first(array_collapse($e->errors()))], 400);
        }
        // 用户认证的异常,我们需要返回 401 的 http code 和错误信息
        if ($e instanceof UnauthorizedHttpException) {
            return response(['msg' => $e->getMessage(), 'code' => 401], 401);
        }

        if ($e instanceof TokenBlacklistedException) {
            return response(['msg' => $e->getMessage(), 'code' => 401], 401);
        }


        return parent::render($request, $e);
    }

我们再一次请求刷新token

pCEYIlF.png

The token has been blacklisted” 说明这个token已经不可用了。我们需要重新登陆

9、用户登陆中间件开发

php artisan make:middleware AuthJwtToken

中间件代码如下:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class AuthJwtToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {

        // 检查此次请求中是否带有 token,如果没有则抛出异常。
        $this->checkForToken($request);

        // 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException  异常
        try {
            // 检测用户的登录状态,如果正常则通过
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            throw new UnauthorizedHttpException('jwt-auth', '未登录');
        } catch (TokenExpiredException $exception) {
            // 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中
            try {
                // 刷新用户的 token
                $token = $this->auth->refresh();
                // 使用一次性登录以保证此次请求的成功
                auth('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
                // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }

        // 在响应头中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);

    }
}

其他

postman使用技巧

每次请求都要携带token,所以我们在一个地方设置全局token并保存后面,每次请求都会自动携带

pCEYTOJ.png

参考资料

jwt官方文档

Laravel5.5使用jwt完成token认证 - 车车大人 - 博客园

Search

    Table of Contents