LaravelJWT过期怎么刷新

wen PHP项目 1

Laravel JWT过期怎么刷新?一文详解Token刷新机制与实战方案

目录导读

  1. JWT过期刷新核心概念
  2. Laravel JWT刷新机制原理
  3. 基础刷新实现(Tyymon/JWT-Auth)
  4. 自动刷新与黑名单策略
  5. 客户端刷新流程设计
  6. 常见问题与问答

JWT过期刷新核心概念

在Laravel开发中,JWT(JSON Web Token)常用于API认证,但默认Token存在过期时间,当Token过期后,用户请求被拒绝(通常返回401状态码),此时需要通过“刷新令牌”机制,让用户无需重新登录即可获取新Token。

LaravelJWT过期怎么刷新

核心点

  • Access Token(短期,如15分钟)
  • Refresh Token(长期,如7天)
  • 刷新操作使用Refresh Token生成新的Access Token

Laravel JWT刷新机制原理

Laravel常用的JWT包是 tymon/jwt-auth,其刷新机制基于以下流程:

  1. 用户登录时,后端发放 access_tokenrefresh_token(实际上jwt-auth默认只返回一个token,需手动扩展)
  2. 当Access Token过期,客户端携带Refresh Token请求 /api/auth/refresh 端点
  3. 后端验证Refresh Token有效性(未过期、未加入黑名单)
  4. 生成新的Access Token并返回

注意:jwt-auth的 refresh() 方法默认会黑名单原Token,防止重复使用。


基础刷新实现(Tyymon/JWT-Auth)

步骤1:安装与配置

composer require tymon/jwt-auth:^2.0
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

配置 config/jwt.php 中的 ttl(Access Token生存时间)和 refresh_ttl(Refresh Token生存时间)。

步骤2:创建刷新路由与控制器

// routes/api.php
Route::post('auth/refresh', [AuthController::class, 'refresh']);
// app/Http/Controllers/AuthController.php
use Tymon\JWTAuth\Facades\JWTAuth;
public function refresh(Request $request)
{
    try {
        // 从请求中获取当前token(旧token)
        $oldToken = JWTAuth::getToken();
        if (!$oldToken) {
            return response()->json(['error' => 'Token not provided'], 401);
        }
        // 刷新token(旧token自动加入黑名单)
        $newToken = JWTAuth::refresh($oldToken);
        return response()->json([
            'access_token' => $newToken,
            'token_type' => 'bearer',
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
        // Token已过期且无法刷新(例如refresh_ttl也过期)
        return response()->json(['error' => 'Token is expired, please re-login'], 401);
    } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
        return response()->json(['error' => 'Token invalid'], 401);
    }
}

步骤3:中间件保护路由

// 在路由中添加JWT中间件
Route::middleware('jwt.auth')->group(function () {
    Route::post('auth/refresh', [AuthController::class, 'refresh']);
});

关键点jwt.auth 中间件会验证Token是否过期,如果过期则抛出 TokenExpiredException,此时可在全局异常处理中返回401,客户端再调用刷新接口。


自动刷新与黑名单策略

1 黑名单机制

jwt-auth默认会在刷新时将旧Token加入黑名单,这意味着:

  • 一个Token只能被刷新一次(除非使用 setRefreshFlow() 标记)
  • 如果恶意用户盗用Refresh Token,原用户将失效

配置黑名单宽限期

// config/jwt.php
'blacklist_grace_period' => 0, // 单位秒,0表示立即黑名单

2 自动刷新中间件

为了避免客户端频繁手动刷新,可创建中间件自动检测并刷新Token:

// app/Http/Middleware/JwtAutoRefresh.php
public function handle($request, Closure $next)
{
    try {
        $token = JWTAuth::parseToken()->authenticate();
        return $next($request);
    } catch (TokenExpiredException $e) {
        try {
            $newToken = JWTAuth::refresh(JWTAuth::getToken());
            // 将新token放入响应header
            $response = $next($request);
            $response->headers->set('Authorization', 'Bearer ' . $newToken);
            return $response;
        } catch (TokenExpiredException $e) {
            return response()->json(['error' => 'Refresh token expired'], 401);
        }
    }
}

注意:自动刷新会影响性能,建议仅在关键路由使用。


客户端刷新流程设计

1 双Token模式(推荐)

  • 后端返回 access_token + refresh_token
  • 客户端存储两者(refresh_token 存到httpOnly cookie)
  • 当API返回401时,客户端自动用 refresh_token 请求刷新接口
  • 获取新 access_token 后重试原请求

示例代码(Axios拦截器)

axios.interceptors.response.use(
    response => response,
    async error => {
        const originalRequest = error.config;
        if (error.response.status === 401 && !originalRequest._retry) {
            originalRequest._retry = true;
            try {
                const { data } = await axios.post('/api/auth/refresh', {
                    refresh_token: localStorage.getItem('refresh_token')
                });
                localStorage.setItem('access_token', data.access_token);
                originalRequest.headers.Authorization = `Bearer ${data.access_token}`;
                return axios(originalRequest);
            } catch (refreshError) {
                // 刷新失败,跳转登录
                window.location.href = '/login';
                return Promise.reject(refreshError);
            }
        }
        return Promise.reject(error);
    }
);

2 单Token模式(简易)

仅使用一个Token,但设置较长的TTL(如1天),过期后重新登录,适合管理后台等低安全场景。


常见问题与问答

Q1:刷新Token时,旧的Access Token还能用吗?

A:默认jwt-auth会黑名单旧Token,使其立即失效,如果希望在黑名单前有一段宽限期(防止并发请求),可设置 'blacklist_grace_period' => 60(秒)。

Q2:Refresh Token泄露了怎么办?

A:

  • 服务端可记录Refresh Token的设备指纹(如User-Agent)
  • 一旦发现异常,立即将该Refresh Token加入黑名单(jwt-auth不支持直接黑名单字符串,需自定义)
  • 推荐使用 laravel-sanctum 替代,它原生支持Token撤销

Q3:为什么刷新后返回的Token与旧Token完全相同?

A:可能是 JWT_BLACKLIST_ENABLED=false 或黑名单被禁用,检查 .envJWT_BLACKLIST_ENABLED 是否为 true

Q4:多设备登录如何保证刷新不互踢?

A:

  • 每个设备生成独立Token,刷新时只黑名单当前设备Token
  • 使用Redis存储设备与Token映射,刷新时删除旧映射

Q5:如何让Refresh Token长期有效且不被盗用?

A:

  • 使用 jwt.refresh_ttl 控制最长有效期(如7天)
  • 结合 JWT_BLACKLIST_GRACE_PERIOD 防止并发问题
  • 服务端记录Refresh Token的最后使用时间,超过阈值要求重新认证

Laravel JWT刷新主要依赖 tymon/jwt-authrefresh() 方法,结合黑名单机制确保安全性,实际项目建议采用双Token + 前端拦截器模式实现无感刷新,对于更高安全要求,可考虑使用 laravel/sanctum 或OAuth2方案。

核心优化点

  • 合理设置TTL(Access 15分钟,Refresh 7天)
  • 启用黑名单 + 宽限期
  • 客户端使用拦截器自动重试
  • 记录Refresh Token的使用日志用于审计

通过以上方案,即可在Laravel应用中实现安全高效的JWT过期刷新机制。

抱歉,评论功能暂时关闭!