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

Laravel Sanctum用户模拟功能仅单次请求有效问题求助

Laravel Jetstream(Sanctum)用户模拟刷新跳转登录问题(密码不同时触发)

我基于Laravel Jetstream(Sanctum身份验证)实现简易用户模拟功能,目前遇到一个问题:

  • 当模拟者与被模拟者密码相同时,功能完全正常,页面可多次刷新
  • 当两者密码不同时,模拟后能正常加载仪表盘,但后续刷新页面会直接跳转到登录页

核心代码如下:

模拟控制器

public function start(Request $request, $userId)
{
    // 验证当前用户是否有模拟权限
    if (!Auth::user()->canImpersonate()) {
        abort(403, 'Unauthorized action.');
    }

    Session::put('impersonate', $userId);

    return redirect()->route('dashboard');
}
public function stop()
{
    // 清除会话中的模拟数据
    session()->forget('impersonate');

    return redirect()->route('dashboard');
}

模拟中间件

public function handle(Request $request, Closure $next): Response
{
    
    // 检查是否处于模拟状态
    if (Session::has('impersonate')) {
        $impersonatedUserId = Session::get('impersonate');

        // 查找并设置被模拟用户
        $impersonatedUser = \App\Models\User::find($impersonatedUserId);
        if ($impersonatedUser && $impersonatedUser->canBeImpersonated()) {
            Auth::setUser($impersonatedUser);
        }
    }

    return $next($request);
}

我曾尝试在控制器start方法或中间件中添加以下代码,但未解决问题:

session()->put([
    'password_hash_sanctum' => $impersonatedUser->getAuthPassword(),
]);

已知lab404/laravel-impersonate等第三方包已停止维护,求排查思路或解决方案。


问题根源

Jetstream(Sanctum)的AuthenticateSession中间件会验证会话中的password_hash_sanctum值与当前认证用户的密码哈希是否匹配,用于防止用户密码变更后会话失效。当切换到密码不同的用户时,原会话中留存的是模拟者的密码哈希,刷新时Sanctum验证不通过,就会强制触发登录跳转。

解决方案

1. 修正中间件逻辑

在设置认证用户的同时,同步更新会话中的密码哈希值,确保Sanctum验证时匹配:

public function handle(Request $request, Closure $next): Response
{
    if (Session::has('impersonate')) {
        $impersonatedUserId = Session::get('impersonate');
        $impersonatedUser = \App\Models\User::find($impersonatedUserId);
        
        if ($impersonatedUser && $impersonatedUser->canBeImpersonated()) {
            Auth::setUser($impersonatedUser);
            // 同步更新会话密码哈希
            session()->put('password_hash_sanctum', $impersonatedUser->getAuthPassword());
        }
    }

    return $next($request);
}

2. 调整中间件执行顺序

必须确保你的模拟中间件在Sanctum的AuthenticateSession中间件之前执行。打开app/Http/Kernel.php,修改web中间件组的顺序:

protected $middlewareGroups = [
    'web' => [
        // ...其他中间件
        \App\Http\Middleware\YourImpersonateMiddleware::class, // 替换为你的模拟中间件类名
        \Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
        // ...其他中间件
    ],
];

3. 优化控制器start方法(可选)

在启动模拟时提前设置会话密码哈希,避免第一次请求后才更新:

public function start(Request $request, $userId)
{
    if (!Auth::user()->canImpersonate()) {
        abort(403, 'Unauthorized action.');
    }

    $impersonatedUser = \App\Models\User::findOrFail($userId);
    if (!$impersonatedUser->canBeImpersonated()) {
        abort(403, 'This user cannot be impersonated.');
    }

    Session::put('impersonate', $userId);
    session()->put('password_hash_sanctum', $impersonatedUser->getAuthPassword());

    return redirect()->route('dashboard');
}

验证逻辑说明

Sanctum的AuthenticateSession中间件会在每个请求时校验password_hash_sanctum与当前用户密码哈希的一致性,不匹配则销毁会话。因此必须保证在该中间件执行前,已完成用户切换和密码哈希的同步更新。

内容的提问来源于stack exchange,提问作者Todd Horst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:53:19