Laravel 11中如何通过中间件阻止API重定向(无需Accept头)
问题:Laravel API未认证时重定向,无需Accept头解决方法
我试图通过中间件覆盖API默认行为,在不依赖请求头的情况下阻止未认证时的重定向,实现了中间件并配置到routes/api.php,但直接请求/api/user时仍然出现302重定向到登录页。
中间件代码:
namespace App\Http\Middleware; use Closure; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; class ApiMiddleware { /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next * @return Response */ public function handle(Request $request, Closure $next): Response { Log::info(__FUNCTION__); $response = $next($request); if ($response->getStatusCode() === 302 || $response->getStatusCode() === 301) { return new JsonResponse(['msg'=>"Unauthorized"], 401); } return $response; } }
路由配置(routes/api.php):
use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Middleware\ApiMiddleware; Route::middleware([ApiMiddleware::class])->group(function (){ // 生成token Route::put('/token',[\App\Http\Controllers\API\SaasUserController::class,'login']) ->name('api.login'); Route::middleware('auth:sanctum')->group(function (){ Route::get('/user', function (Request $request) { return $request->user(); }); // 其他需要认证的路由 }); });
请求日志(Insomnia):
> GET /api/user HTTP/1.1 > Host: 172.161.5.2 > User-Agent: insomnia/2022.6.0 > Cookie: my_session=eyJpdiI6IjBxWVJkMlJobTREYVFuMk9wZ2xnV0E9PSIsInZhbHVlIjoiRmlRbnlwSlhBbE55a0FJS21hWE9reCs0Q1FPOW03bU1vcy9zOFhrZG1QTHlDYmgwV1VqUVgyNFJpUXE3cmxrOWI4d2lFcktDVFMydmxjZ0VmcFF3WGQvOWYvM0V4dXhQb2xya1ppNEVBNnZDMXBQNUJsaDJmV3NBMTdjdmpYREEiLCJtYWMiOiI2MGQ4MDA1ZTU1MTczYjM4NDdkNjFjOTE1ZDg3ZGU4YTUwOTlkOGRhY2EyZTJlZDU2ZTFhMWVlMWIyMjAyYWZiIiwidGFnIjoiIn0%3D; XSRF-TOKEN=xxxx > Accept: */* * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * old SSL session ID is stale, removing * Mark bundle as not supporting multiuse < HTTP/1.1 302 Found < Server: nginx < Content-Type: text/html; charset=UTF-8 < Transfer-Encoding: chunked < Connection: keep-alive < X-Powered-By: PHP/8.2.17 < Cache-Control: no-cache, private < Date: Thu, 28 Mar 2024 10:29:17 GMT < Location: https://172.161.5.2/login < Access-Control-Allow-Origin: * * Ignoring the response-body * Received 358 B chunk * Connection #6 to host 172.161.5.2 left intact * Issue another request to this URL: 'https://172.161.5.2/login' * Found bundle for host 172.161.5.2: 0x3f5c038f58c0 [serially] * Can not multiplex, even if we wanted to! * Re-using existing connection! (#6) with host 172.161.5.2 * Connected to 172.161.5.2 (172.161.5.2) port 443 (#6) > GET /login HTTP/1.1 > Host: 172.161.5.2 > User-Agent: insomnia/2022.6.0 > Cookie: my_session=eyJpdiI6IjBxWVJkMlJobTREYVFuMk9wZ2xnV0E9PSIsInZhbHVlIjoiRmlRbnlwSlhBbE55a0FJS21hWE9reCs0Q1FPOW03bU1vcy9zOFhrZG1QTHlDYmgwV1VqUVgyNFJpUXE3cmxrOWI4d2lFcktDVFMydmxjZ0VmcFF3WGQvOWYvM0V4dXhQb2xya1ppNEVBNnZDMXBQNUJsaDJmV3NBMTdjdmpYREEiLCJtYWMiOiI2MGQ4MDA1ZTU1MTczYjM4NDdkNjFjOTE1ZDg3ZGU4YTUwOTlkOGRhY2EyZTJlZDU2ZTFhMWVlMWIyMjAyYWZiIiwidGFnIjoiIn0%3D; XSRF-TOKEN=XXXXXX > Accept: */* * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Server: nginx < Content-Type: text/html; charset=UTF-8 < Transfer-Encoding: chunked < Connection: keep-alive < X-Powered-By: PHP/8.2.17 < Cache-Control: no-cache, private < Date: Thu, 28 Mar 2024 10:29:17 GMT * Replaced cookie XSRF-TOKEN="XXXXXX" for domain 172.161.5.2, path /, expire 1711628957 < Set-Cookie: XSRF-TOKEN=XXXXXX; expires=Thu, 28 Mar 2024 12:29:17 GMT; Max-Age=7200; path=/; secure; samesite=lax * Replaced cookie my_session="eyJpdiI6InBXcHQrVnc1QVVVbVBpckd1eE5VS0E9PSIsInZhbHVlIjoidEF5RGlHRTIvVFdGNnBodVdMWC9UYWYydUVzanJFOTRBcjN3WTZuYUgvSHpFempNWUZiZjVHSGJCandHdUZFakNxRFpwbGo1WGxYVWpnSjA3VlF1ZnZKZENtUWFJUENwMW9EMyt6UmpidjZWVzZTdkIrekk4d24xK0R4Wi9IeloiLCJtYWMiOiI3MzE3YmU3OGY1MTc0NTJmYjVlZTZkMDNiOWE1YTkwNGFiNGMyNmNiOWUwMThmNTFkNDg3ZWVkNGMyNGIyOGNmIiwidGFnIjoiIn0%3D" for domain 172.161.5.2, path /, expire 1711628957 < Set-Cookie: my_session=eyJpdiI6InBXcHQrVnc1QVVVbVBpckd1eE5VS0E9PSIsInZhbHVlIjoidEF5RGlHRTIvVFdGNnBodVdMWC9UYWYydUVzanJFOTRBcjN3WTZuYUgvSHpFempNWUZiZjVHSGJCandHdUZFakNxRFpwbGo1WGxYVWpnSjA3VlF1ZnZKZENtUWFJUENwMW9EMyt6UmpidjZWVzZTdkIrekk4d24xK0R4Wi9IeloiLCJtYWMiOiI3MzE3YmU3OGY1MTc0NTJmYjVlZTZkMDNiOWE1YTkwNGFiNGMyNmNiOWUwMThmNTFkNDg3ZWVkNGMyNGIyOGNmIiwidGFnIjoiIn0%3D; expires=Thu, 28 Mar 2024 12:29:17 GMT; Max-Age=7200; path=/; httponly; samesite=lax
请问是否有无需提供Accept头的方法来解决这个重定向问题?
解决方案
问题核心是中间件执行顺序:auth:sanctum中间件在你的ApiMiddleware之前执行,认证失败时直接返回重定向响应,你的中间件没机会处理该响应。以下是两种无需依赖Accept头的解决方法:
方法1:调整中间件优先级,让ApiMiddleware先执行
在app/Http/Kernel.php中,找到或新增$middlewarePriority数组,将ApiMiddleware放在Authenticate(auth中间件的底层类)之前:
protected $middlewarePriority = [ \App\Http\Middleware\ApiMiddleware::class, \Illuminate\Auth\Middleware\Authenticate::class, // 其他中间件... ];
这样你的中间件会先于认证中间件处理响应,捕获到302重定向后替换为401 JSON响应。
方法2:自定义Sanctum认证失败逻辑(推荐)
直接修改Sanctum的认证失败行为,从根源上避免重定向:
- 创建自定义认证中间件,继承Sanctum的认证中间件:
namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; class ApiAuthenticate extends Middleware { /** * Handle an unauthenticated user. * * @param \Illuminate\Http\Request $request * @param array $guards * @return void * * @throws \Illuminate\Auth\AuthenticationException */ protected function redirectTo($request) { // 针对API路由直接返回401 JSON响应 if ($request->is('api/*')) { return response()->json(['msg' => 'Unauthorized'], 401); } } }
- 在
app/Http/Kernel.php的$routeMiddleware中替换原有的auth:sanctum:
protected $routeMiddleware = [ // ... 'auth:sanctum' => \App\Http\Middleware\ApiAuthenticate::class, // ... ];
- 路由配置可简化为(无需再使用原
ApiMiddleware):
use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::group(function (){ Route::put('/token',[\App\Http\Controllers\API\SaasUserController::class,'login']) ->name('api.login'); Route::middleware('auth:sanctum')->group(function (){ Route::get('/user', function (Request $request) { return $request->user(); }); // 其他需要认证的路由 }); });
两种方法都能让API在未认证时直接返回401 JSON响应,无需依赖请求头。方法2更直接,是从认证逻辑层面修改响应,推荐使用。
内容的提问来源于stack exchange,提问作者Dimitrios Desyllas
相关产品推荐
相关产品推荐

