如何在 Laravel 11 上使用中间件防止 API 中的重定向?

问题描述 投票:0回答:1

根据此答案,我需要覆盖 api 调用的默认行为,并防止重定向,无论使用中间件的标头:

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 (){

    // Generating the 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();
        });
        // Misc routes that need authentication
    });
});

但是通过对

/api/user
进行简单的 GET 操作,我可以根据失眠日志获得重定向:


> 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


有没有办法覆盖它而不需要提供接受标头?

php laravel redirect laravel-11
1个回答
0
投票

为了实现这一目标,您需要在

bootstrap/app.php
全局应用中间件,如下所示:

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->api(prepend: [
            \App\Http\Middleware\ApiMiddleware::class
        ]);
        // Rest of middleware bootstrapping goes here
    })
    ->withExceptions(function (Exceptions $exceptions) {
       // Code ommited here if any
    })->create();

还可以以服务器接受的格式发送 401 响应来进行改进:


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
    {
        $response = $next($request);

        if ($response->getStatusCode() === 302 || $response->getStatusCode() === 301) {

           $accept=$request->header('Accept')??"";
           $accept=trim(preg_replace(";.*","",$accept));
           if(!empty($accept) && $accept!='application/json'){
                    $content="Unautorized";

                    switch ($accept){
                        case "application/html":
                        case "text/html":
                            $content="<!DOCTYPE html><html><head><tilte>Unautorized</tilte></head><body>Unautorized</body></html>";
                            break;
                        case 'application/xml':
                        case 'text/xml':
                            $content="<xml><msg>Unautorized</msg></xml>";
                            break;
                        default:
                            $accept="text/plain";
                    }
                   return new Response($content,401,['Content-Type',$accept]);
            }


            return new JsonResponse(['msg'=>"Unauthorized"], 401);
        }

        return $response;
    }
}


或者强制执行特定类型(在我的例子中是 Json):

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
    {
        if(!$request->wantsJson()){
          new Response("",400);
        }

        $response = $next($request);
        return $response;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.