统一会话失效接口响应

This commit is contained in:
pllx
2026-05-05 21:55:48 +08:00
parent 725a38eac3
commit 64945a973e
2 changed files with 57 additions and 18 deletions
+29 -17
View File
@@ -1,5 +1,10 @@
<?php <?php
/**
* 文件功能:Laravel 应用启动配置。
* 负责注册路由、中间件别名、代理信任规则与全局异常响应格式。
*/
use Illuminate\Foundation\Application; use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Foundation\Configuration\Middleware;
@@ -38,8 +43,12 @@ return Application::configure(basePath: dirname(__DIR__))
$middleware->redirectGuestsTo('/'); $middleware->redirectGuestsTo('/');
}) })
->withExceptions(function (Exceptions $exceptions): void { ->withExceptions(function (Exceptions $exceptions): void {
$isChatAjaxRequest = static function (Request $request): bool { $isJsonSessionRequest = static function (Request $request): bool {
return $request->expectsJson() && $request->is( if ($request->expectsJson() || $request->ajax()) {
return true;
}
return $request->is(
'room/*/send', 'room/*/send',
'room/*/heartbeat', 'room/*/heartbeat',
'room/*/leave', 'room/*/leave',
@@ -51,25 +60,28 @@ return Application::configure(basePath: dirname(__DIR__))
); );
}; };
// 聊天室 AJAX 接口:CSRF token 过期(419)时,返回 JSON 提示而非重定向 $expiredSessionResponse = static function () {
// 防止浏览器收到 302 后以 GET 方式重请求只允许 POST 的路由,产生 405 错误 return response()->json([
$exceptions->render(function (TokenMismatchException $e, Request $request) use ($isChatAjaxRequest) { 'status' => 'error',
if ($isChatAjaxRequest($request)) { 'code' => 'SESSION_EXPIRED',
return response()->json([ 'message' => '登录状态已失效,请刷新页面后重新登录。',
'status' => 'error', 'reload' => true,
'message' => '页面已过期,请刷新后重试。', 'login_url' => route('home'),
], 419); ], 419);
};
// CSRF token 失效通常意味着页面还停留在旧会话里;JSON 请求统一返回业务提示,避免泄露框架异常堆栈。
$exceptions->render(function (TokenMismatchException $e, Request $request) use ($expiredSessionResponse, $isJsonSessionRequest) {
if ($isJsonSessionRequest($request)) {
return $expiredSessionResponse();
} }
}); });
// Laravel 在某些环境下会先把 TokenMismatchException 包装成 419 HttpException // Laravel 在某些环境下会先把 TokenMismatchException 包装成 419 HttpException
// 这里补一层兜底,确保聊天接口始终返回稳定 JSON,而不是默认 HTML 错误页 // 这里补一层兜底,确保接口始终返回稳定 JSON,而不是默认异常结构
$exceptions->render(function (HttpExceptionInterface $e, Request $request) use ($isChatAjaxRequest) { $exceptions->render(function (HttpExceptionInterface $e, Request $request) use ($expiredSessionResponse, $isJsonSessionRequest) {
if ($e->getStatusCode() === 419 && $isChatAjaxRequest($request)) { if ($e->getStatusCode() === 419 && $isJsonSessionRequest($request)) {
return response()->json([ return $expiredSessionResponse();
'status' => 'error',
'message' => '页面已过期,请刷新后重试。',
], 419);
} }
}); });
})->create(); })->create();
+28 -1
View File
@@ -25,6 +25,7 @@ use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Facades\Broadcast; use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
@@ -779,10 +780,36 @@ class ChatControllerTest extends TestCase
\Illuminate\Testing\TestResponse::fromBaseResponse($response)->assertStatus(419)->assertJson([ \Illuminate\Testing\TestResponse::fromBaseResponse($response)->assertStatus(419)->assertJson([
'status' => 'error', 'status' => 'error',
'message' => '页面已过期,请刷新后重试。', 'code' => 'SESSION_EXPIRED',
'message' => '登录状态已失效,请刷新页面后重新登录。',
'reload' => true,
]); ]);
} }
/**
* 测试掉线后的普通 JSON 接口遇到 CSRF 失效时,不会泄露框架异常结构。
*/
public function test_json_token_mismatch_exception_renders_session_expired_response(): void
{
$request = Request::create('/user/profile', 'POST', server: [
'HTTP_ACCEPT' => 'application/json',
]);
$response = $this->app->make(\Illuminate\Contracts\Debug\ExceptionHandler::class)
->render($request, new TokenMismatchException('CSRF token mismatch.'));
\Illuminate\Testing\TestResponse::fromBaseResponse($response)
->assertStatus(419)
->assertJson([
'status' => 'error',
'code' => 'SESSION_EXPIRED',
'message' => '登录状态已失效,请刷新页面后重新登录。',
'reload' => true,
])
->assertJsonMissingPath('exception')
->assertJsonMissingPath('trace');
}
/** /**
* 测试房间公告更新广播中的动态内容会被转义。 * 测试房间公告更新广播中的动态内容会被转义。
*/ */