laravel sanctum SPA 身份验证受保护的路由返回 { "message" : "unauthenticated"}

lar*_*onk 5 php oauth laravel vue.js

我正在做一个大项目,它有一个用于 API 的 laravel 后端和一个单独的 SPA(vue-cli 脚手架)。它们位于同一个顶级域中,laravel 项目在域 (mm) 上运行,Vue Spa 在 (vue.mm:8080) 上运行

我的问题是:

  • auth:sanctum 受保护的路由不起作用,它们返回 ({ "message" : "Unauthenticated" })

什么有效:

  • 如果给定正确的密码和电子邮件,路由 /api/login 可以完美运行(它会发送响应({“消息”:“登录成功”}))

我的代码片段:

.env(它只是基于文档所需的部分,由于安全问题我无法显示其他部分)

BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=cookie
SESSION_LIFETIME=120
SESSION_DOMAIN=.m.m
SANCTUM_STATEFUL_DOMAINS=vue.m.m
Run Code Online (Sandbox Code Playgroud)

后端

UserController => 用于身份验证

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Auth;
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class UserController extends Controller
{
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        $user = $this->create($request->all());

        $this->guard()->login($user);

        return response()->json(['user'=> $user,
                                  'message'=> 'registration successful'
                                ], 200);
    }
      /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:4', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
    protected function guard()
    {
        return Auth::guard();
    }

    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            // Authentication passed...
            return response()->json(['message' => 'Login successful'], 200);
        }
    }

    public function logout()
    {
        Auth::logout();
        return response()->json(['message' => 'Logged Out'], 200);
    }
}
Run Code Online (Sandbox Code Playgroud)

sactum.php

<?php

return [

    'stateful' => explode(',', env(
        'SANCTUM_STATEFUL_DOMAINS',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
    )),
    'expiration' => null,
    'middleware' => [
        'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
        'encrypt_cookies' => \Illuminate\Cookie\Middleware\EncryptCookies::class, 
    ],

];
Run Code Online (Sandbox Code Playgroud)

身份验证文件

<?php

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    'password_timeout' => 10800,

];
Run Code Online (Sandbox Code Playgroud)

用户模型

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasFactory, Notifiable,HasApiTokens;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
Run Code Online (Sandbox Code Playgroud)

api.php

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return "protected route";
});

Route::post("/login","App\Http\Controllers\UserController@login");
Run Code Online (Sandbox Code Playgroud)

cors.php(出于开发目的,我允许处理所有传入请求)和(凭据设置为 true)

'paths' => ['*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => [],

'max_age' => 0,

'supports_credentials' => true,
Run Code Online (Sandbox Code Playgroud)

内核文件

protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

 'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
Run Code Online (Sandbox Code Playgroud)

前端

公理

axios.defaults.withCredentials = true;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.baseURL = 'http://m.m/api';
Run Code Online (Sandbox Code Playgroud)

Vue(这是处理发送登录请求的方法)

login(evt) {
      evt.preventDefault();
        axios.get("/sanctum/csrf-cookie", { baseURL: "http://m.m" }).then(() => {
          axios
            .post(
              "/login",
              { email: "john@doe.com", password: "password" },
            )
            .then(() => {
              const res = axios.get("/user");
              console.log(res.data);
            });
        });
      };
    }
Run Code Online (Sandbox Code Playgroud)

从 chrome的浏览器 网络选项卡中获得的信息

这是作为 cookie (图像)存储在浏览器中的内容

小智 0

我有同样的问题。我解决如下:

在api路由中定义

Route::get('/unauthenticated', function () {
    return response()->json(["message" : "unauthenticated"]);
})->name('api.unauthenticated');
Run Code Online (Sandbox Code Playgroud)

redirectTo现在在中间件的方法中Authenticate.php添加这一行

if ($request->is('api/*') || $request->is('api'))
 return route('api.unauthenticated');
Run Code Online (Sandbox Code Playgroud)

就是这样。这个对我有用