Laravel 和 Sanctum 基于令牌 cookie 的身份验证返回未经身份验证的

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

我登录;

curl --location 'http://localhost/api/login' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Cookie: auth_token=eyJpdiI6InhKeXBxNG9VV1hNVDBJaWZ3V2VuNVE9PSIsInZhbHVlIjoiUWZWeGlFc1hzTHFuZWZ6SzVPVzM1MmhHSUFxL1N6K0FYZkNZM3FkUVh1SHF0akUwSTA2WUZjMjY0dTRsUFY4SjlRM3BlemFBejRraEkvd0Y0aHV3REpXVkdhZkE5ZWxhMFNzSzJjN09sUUMrdk9va1VBbjlQQm9ac21uaW0rUnIiLCJtYWMiOiIyZjBkOTNhODNiOTc2MGFkMzkzMmMyN2FlNDQ0OWE4NDNjMGJmMTRlMWJiMGI1MzYyYjNmMTMxYmVlNmFiZWRjIiwidGFnIjoiIn0%3D; laravel_session=iZ2O2glj1yVL8mskNGkgAJTLMRZQaFxMmaeyVBFr' \
--data-raw '{
    "email": "[email protected]",
    "password": "password"
}'

/routes/api.php

Route::post('/login', [AuthController::class, 'login'])->name('login');

/AuthController.php

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Laravel\Sanctum\HasApiTokens;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $secure = app()->environment('production');

        $request->validate([
            'email' => 'required|email',
            'password' => 'required|min:6',
        ]);

        $user = User::where('email', $request->email)->first();

        if ($user && Hash::check($request->password, $user->password)) {

            $request->session()->regenerate();
            $token = $user->createToken('FacilityManagementApp')->plainTextToken;

            return response()->json([
                'message' => 'Logged in successfully.',
                'user' => [
                    'id' => $user->id,
                    'name' => $user->name,
                    'email' => $user->email,
                    'role' => $user->role,
                ]
            ], 200)->cookie(
                'auth_token',
                $token,                     
                120,                         
                '/',                        
                null,                       
                $secure,                    
                true                        
            );
        }

        return response()->json(['error' => 'Unauthorized'], 401);
    }
}

我收到回复;

{
    "message": "Logged in successfully.",
    "user": {
        "id": 2,
        "name": "Mertie Wisozk Sr.",
        "email": "[email protected]",
        "role": "user"
    }
}

并且从

personal_access_tokens
我观察到;

[
  {
    "id": 64,
    "tokenable_type": "App\\Models\\User",
    "tokenable_id": 2,
    "name": "FacilityManagementApp",
    "token": "fcab1bf4ae0bd7b1ebff188af5aa6a692c31c0a8d03a4fdb1f2fc08c5764cb4e",
    "abilities": "[\"*\"]",
    "last_used_at": null,
    "expires_at": null,
    "created_at": "2025-01-06 09:54:25",
    "updated_at": "2025-01-06 09:54:25"
  }
]

所以,一切看起来都不错。然后,我尝试访问受

Sanctum
中间件保护的端点;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    Log::debug('Authenticated User:', [$request->user()]);
    return $request->user();
});

就像这样;

curl --location 'http://localhost/api/user' \
--header 'Cookie: auth_token=eyJpdiI6InhKeXBxNG9VV1hNVDBJaWZ3V2VuNVE9PSIsInZhbHVlIjoiUWZWeGlFc1hzTHFuZWZ6SzVPVzM1MmhHSUFxL1N6K0FYZkNZM3FkUVh1SHF0akUwSTA2WUZjMjY0dTRsUFY4SjlRM3BlemFBejRraEkvd0Y0aHV3REpXVkdhZkE5ZWxhMFNzSzJjN09sUUMrdk9va1VBbjlQQm9ac21uaW0rUnIiLCJtYWMiOiIyZjBkOTNhODNiOTc2MGFkMzkzMmMyN2FlNDQ0OWE4NDNjMGJmMTRlMWJiMGI1MzYyYjNmMTMxYmVlNmFiZWRjIiwidGFnIjoiIn0%3D; laravel_session=iZ2O2glj1yVL8mskNGkgAJTLMRZQaFxMmaeyVBFr'

并得到未经身份验证的响应?我期待

Sanctum
验证
auth_token
cookie 并在响应中为我提供经过身份验证的用户?

这是我的努力和各种配置文件和内容;

File: app/Http/Controllers/AuthController.php
Content:
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Laravel\Sanctum\HasApiTokens;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $secure = app()->environment('production');

        $request->validate([
            'email' => 'required|email',
            'password' => 'required|min:6',
        ]);

        $user = User::where('email', $request->email)->first();

        if ($user && Hash::check($request->password, $user->password)) {

            $request->session()->regenerate();
            $token = $user->createToken('FacilityManagementApp')->plainTextToken;

            return response()->json([
                'message' => 'Logged in successfully.',
                'user' => [
                    'id' => $user->id,
                    'name' => $user->name,
                    'email' => $user->email,
                    'role' => $user->role,
                ]
            ], 200)->cookie(
                'auth_token',         // Cookie name
                $token,                     // Token value
                120,                         // Expiry time in minutes
                '/',                        // Path for which the cookie is valid
                null,                       // Domain (null means current domain)
                $secure,                    // Secure cookie (only transmitted over HTTPS)
                true                        // HttpOnly (cannot be accessed via JavaScript)
            );
        }

        return response()->json(['error' => 'Unauthorized'], 401);
    }




}
----------
File: routes/api.php
Content:
<?php


use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;

Route::post('/login', [AuthController::class, 'login'])->name('login');

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    Log::debug('Authenticated User:', [$request->user()]);
    return $request->user();
});





----------
File: config/sanctum.php
Content:
<?php

use Laravel\Sanctum\Sanctum;

return [

    'cookie' => [
        'name' => 'auth_token',
        'secure' => env('APP_ENV') === 'production',
    ],

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,localhost:5173,127.0.0.1,127.0.0.1:8000,::1',
        Sanctum::currentApplicationUrlWithPort()
    ))),

    'guard' => ['web'],

    'expiration' => 120,

    'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

    'middleware' => [
        'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
        'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
        'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
    ],

];
----------
File: config/cors.php
Content:
<?php

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['http://localhost:5173'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['Authorization', 'Content-Type', 'Cookie'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];

----------
File: config/auth.php
Content:
<?php

return [


    'defaults' => [
        'guard' => env('AUTH_GUARD', 'api'),
        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'sanctum',
            'provider' => 'users',
            'hash' => false,
        ],
    ],


    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],



    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
            'expire' => 60,
            'throttle' => 60,
        ],
    ],



    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),

];
----------
File: bootstrap/app.php
Content:
<?php

use Illuminate\Auth\AuthenticationException;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Middleware\HandleCors;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;

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->use([
            HandleCors::class,
            StartSession::class,
            EnsureFrontendRequestsAreStateful::class,
            EncryptCookies::class,
            ShareErrorsFromSession::class
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (AuthenticationException $e, $request) {

            if ($request->expectsJson()) {
                return response()->json(['message' => 'Unauthenticated'], 401);
            }
            return response('Unauthenticated', 401);
        });

        $exceptions->render(function (NotFoundHttpException $e, $request) {
            Log::debug("Caught NotFoundHttpException in exception handler: " . $e->getMessage());

            if ($request->expectsJson()) {
                return response()->json(['message' => 'Resource not found'], 404);
            }
            return response()->view('errors.404', [], 404);
        });

        $exceptions->render(function (\Exception $e, $request) {
            Log::debug('Request Info:', [
                'headers' => $request->headers->all(),
                'cookies' => $request->cookies->all(),
                'body' => $request->getContent(),
            ]);
            Log::error("Caught uncaught exception in exception handler: " . get_class($e) . ": " . $e->getMessage());

            return response()->json(['message' => 'Something went wrong'], 500);
        });
    })
    ->create();



----------
File: .env
Content:
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:9gWy0+pqvHZwKGzZhZDGf4i4j+yndF24d6ZBO04nTek=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost


SANCTUM_STATEFUL_DOMAINS=localhost,localhost:5173,localhost:3000,127.0.0.1,127.0.0.1:8000,::1



AUTH_GUARD=api
AUTH_PASSWORD_BROKER=users
AUTH_MODEL=App\Models\User


APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database

PHP_CLI_SERVER_WORKERS=4

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=facil_manag_app
DB_USERNAME=laravel
DB_PASSWORD=password

SESSION_DOMAIN=localhost
SESSION_DRIVER=cookie
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_SECURE_COOKIE=false

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=database
CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"




我已经尽可能详细地介绍了我的中间件和配置设置,以便让您全面了解我的方法。如果您能想到其他任何东西 - 我很乐意提供给您。这可能是某个地方的一个简单设置,但我无法找出它当前是什么?帮助。

laravel-11 sanctum
1个回答
0
投票

为 API 请求添加中间件,即将

Authorization Bearer
标头设置为安全 Cookie 值
$token
修复了此问题。

php artisan make:middleware AttachAuthToken

应用程序/Http/中间件

class AttachAuthToken
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next)
    {
        // Retrieve token from cookie
        if ($token = $request->cookie('auth_token')) {
            // Attach the token as a Bearer token in the Authorization header
            $request->headers->set('Authorization', 'Bearer ' . $token);
        }

        return $next($request);
    }

}

bootstrap/app.php

->withMiddleware(function (Middleware $middleware) {
        $middleware->use([
            ...
            AttachAuthToken::class,
            ...
        ]);
    })
© www.soinside.com 2019 - 2024. All rights reserved.