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

Laravel Sanctum测试API密码确认接口遇Session未设置异常

解决Laravel Sanctum API测试密码确认接口时的Session异常问题

我太懂你这个痛点了!用Laravel Sanctum做API认证,测试密码确认接口时偏偏碰到RuntimeException: Session store not set on request的报错,排查后发现是框架自带的密码确认逻辑依赖Session,但API请求本身是无状态的,根本不支持Session操作对吧?

问题根源

框架默认的PasswordConfirmController里的resetPasswordConfirmationTimeout方法是为**Web端(带Session)**设计的,它会向Session存入auth.password_confirmed_at字段来标记用户已确认密码。但API请求遵循无状态原则,默认没有Session支持,所以当你携带密码发起请求时,走到操作Session的步骤就直接抛出异常了。而你发空请求时,验证逻辑先触发,还没到操作Session的环节,所以能正常返回验证错误。

解决方案

根据API无状态的设计原则,推荐两种处理方式:

方案一:自定义密码确认逻辑(推荐生产环境使用)

完全绕过Session,用缓存来实现密码确认的超时机制:

  1. 创建自定义的密码确认控制器
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class PasswordConfirmController extends Controller
{
    public function confirm(Request $request)
    {
        // 验证密码字段
        $request->validate([
            'password' => ['required', 'string'],
        ]);

        // 校验密码是否正确
        if (!Hash::check($request->password, $request->user()->password)) {
            throw ValidationException::withMessages([
                'password' => __('auth.password'),
            ]);
        }

        // 用缓存存储密码确认状态,有效期和框架默认的Session超时一致(默认30分钟)
        cache()->put(
            'password_confirmed_' . $request->user()->id,
            true,
            config('auth.password_timeout', 1800)
        );

        return response()->json(['message' => 'Password confirmed successfully']);
    }
}
  1. 更新API路由
    修改routes/api.php,指向自定义控制器:
use App\Http\Controllers\Auth\PasswordConfirmController;

Route::post('/password-confirmation', [PasswordConfirmController::class, 'confirm'])
    ->middleware('auth:sanctum');
  1. 自定义中间件验证密码确认状态
    如果有需要验证用户已确认密码的接口,创建一个中间件来检查缓存:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsurePasswordIsConfirmed
{
    public function handle(Request $request, Closure $next)
    {
        if (!cache()->get('password_confirmed_' . $request->user()->id)) {
            return response()->json(['message' => 'Please confirm your password first.'], 403);
        }

        return $next($request);
    }
}

记得在app/Http/Kernel.php里注册这个中间件,然后在需要的路由上使用。

方案二:测试时临时启用Session(仅用于测试,不推荐生产)

如果只是为了通过测试,不想修改业务逻辑,可以在测试类里给请求手动绑定Session:

<?php

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ConfirmPasswordControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_confirm_password_confirms()
    {
        $user = User::factory()->create(['password' => bcrypt('password')]);

        // 模拟认证用户并绑定空Session
        $this->actingAs($user, 'sanctum')->withSession([]);

        $response = $this->postJson('/api/password-confirmation', [
            'password' => 'password',
        ]);

        $response->assertStatus(200);
        // 其他断言逻辑
    }
}

这个方法只是临时解决测试问题,不符合API无状态的设计理念,生产环境千万别用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:42:39