You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10:中间件中Session重生成后值未更新的问题

Laravel 10会话更新异常问题排查

场景描述

我正在开发一个Laravel 10应用,使用中间件检查外部API的token是否过期。若token过期,将通过refresh_token生成新token,更新会话中的相关数据(token、refresh_token、expires_at)。

中间件代码

namespace App\Http\Middleware;

use Closure;
use Carbon\Carbon;
use Nanicas\Auth\Services\ThirdPartyAuthService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;

class NanicasAuthenticateMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $auth = $request->session()->get('nanicas');
        if (empty($auth)) {
            return $this->logout($request);
        }

        if (!Carbon::now()->greaterThanOrEqualTo($auth['expires_at_datetime'])) {
            return $next($request);
        }

        $config = config('nanicas');

        $authService = app()->make(ThirdPartyAuthService::class);
        $authResponse = $authService->refreshToken([
            'grant_type' => 'refresh_token',
            'client_id' => $config['CLIENT_ID'],
            'client_secret' => $config['CLIENT_SECRET'],
            'refresh_token' => $auth['refresh_token'],
            'scope' => '',
        ]);

        if (!$authResponse['status']) {
            return $this->logout($request);
        }

        $request->session()->regenerate();
        $request->session()->put('nanicas', $authResponse['body']);

        return $next($request);
    }

    private function logout(Request $request)
    {
        $request->session()->forget('nanicas');

        Auth::logout();

        $request->session()->invalidate();

        return $request->expectsJson() 
            ? response()->json(['message' => 'Token expirado'], 401) 
            : redirect()->route('login');
    }
}

路由配置

Route::middleware('auth.nanicas')->group(function () {
    Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
});

问题核心

Laravel会话有效期为2天,但外部API的token每2小时过期一次。当token过期后刷新HOME页面,会话仍保留旧值——仅在使用$request->session()->regenerate()时出现此问题,移除该代码后一切正常。

疑问:

  1. 为何使用regenerate()时会话未更新为新值?
  2. 注释掉web组中的\App\Http\Middleware\EncryptCookies::class中间件会生成一些随机ID,这是否与该问题相关?

问题解答

1. regenerate()导致会话数据不更新的原因

你当前的代码执行顺序错误:先调用regenerate()再更新会话数据。Laravel的regenerate()方法会执行以下操作:

  • 创建新的会话ID
  • 将当前会话的所有数据复制到新会话
  • 销毁旧会话

这意味着你在regenerate()之后调用put()写入的新数据,仅存在于当前请求的临时会话实例中,但新会话的Cookie还未发送给浏览器。下一次请求时,浏览器仍携带旧会话ID,读取到的自然还是旧数据。

修正方案:调整执行顺序,先更新会话数据,再重新生成会话ID:

// 先写入新的token数据
$request->session()->put('nanicas', $authResponse['body']);
// 再重新生成会话ID,此时新数据会被复制到新会话中
$request->session()->regenerate();

2. 关于EncryptCookies中间件的疑问

注释掉EncryptCookies后出现的随机ID,是Laravel为未加密的会话Cookie自动添加的签名(用于验证数据未被篡改),这和你当前的会话数据不更新问题没有直接关联。但强烈建议不要禁用该中间件——它是Laravel会话安全的核心保障,禁用后会话数据会以明文形式存储在Cookie中,极易被篡改或窃取。

额外优化建议

  • 使用regenerate(true)替代regenerate(),该方法会立即使旧会话失效,进一步防范会话固定攻击。
  • 为第三方API的refreshToken请求添加异常捕获逻辑,避免API请求失败导致页面崩溃。

内容的提问来源于stack exchange,提问作者José Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:36:07