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

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的认证失败行为,从根源上避免重定向:

  1. 创建自定义认证中间件,继承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);
        }
    }
}
  1. 在app/Http/Kernel.php的$routeMiddleware中替换原有的auth:sanctum:
protected $routeMiddleware = [
    // ...
    'auth:sanctum' => \App\Http\Middleware\ApiAuthenticate::class,
    // ...
];
  1. 路由配置可简化为(无需再使用原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:15:55