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

Laravel Sanctum + Vue3 登录后/api/user接口401未授权问题求助

Laravel Sanctum + Vue3 401未授权问题排查与解决

问题现象

登录后调用http://localhost:8000/api/user(GET请求)返回401状态码,但数据库personal_access_tokens表已生成对应令牌,Postman手动携带令牌调用该接口可正常获取用户信息。

相关配置

Laravel路由配置

<?php

Route::prefix('auth')->group(function () {
    Route::post('/login', LoginController::class);
    Route::post('/logout', LogoutController::class)->middleware('auth:sanctum');
    Route::post('/register', RegisterController::class);
});

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

LoginController代码

<?php

class LoginController extends Controller
{
    public function __invoke(LoginRequest $request){
        $user = User::where('email', $request->email)->first();

        if(!$user || !Hash::check($request->password, $user->password)){
            throw ValidationException::withMessages([
                'email' => ['The credentials you entered are incorrect.']
            ]);
        }

        return response()->json([
            'user' => $user,
            'token' => $user->createToken('laravel_api_token')->plainTextToken
        ]);
    }
}

CORS配置

<?php

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie', 'auth/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];

Sanctum配置

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

.env中的Stateful Domains配置

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

Vue3 API调用代码

import api from "./api";
export const csrfCookie = () => api.get("/sanctum/csrf-cookie");
export const login = (credentials) => api.post("/api/auth/login", credentials);
export const register = (user) => api.post("/api/auth/register", user);
export const logout = () => api.post("/api/auth/logout");
console.log(login);
export const getUser = () => api.get("/api/user");

Vue3 Pinia Auth Store代码

import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { csrfCookie, login, register, logout, getUser } from "../http/auth-api";
export const useAuthStore = defineStore("authStore", () => {
  const user = ref(null);
  const isLoggedIn = computed(() => !!user.value);
  const fetchUser = async () => {
    const { data } = await getUser();
    user.value = data;
  };
  const handleLogin = async (credentials) => {
    await csrfCookie();
    await login(credentials);
    await fetchUser();
  };
  const handleRegister = async (newUser) => {
    await register(newUser);
    await handleLogin({
      email: newUser.email,
      password: newUser.password,
    });
  };
  const handleLogout = async () => {
    await logout();
    user.value = null;
  };
  return {
    user,
    isLoggedIn,
    fetchUser,
    handleLogin,
    handleRegister,
    handleLogout,
  };
});

Axios配置

axios.defaults.withCredentials = true;
const api = axios.create({
    baseURL: import.meta.env.VITE_BASE_URL,
})

export default api

解决方案

核心问题:前端未自动携带令牌到请求头

Postman手动携带令牌能成功,但你的前端代码仅在登录时获取了令牌,却未配置自动将令牌附加到请求头中。

  1. 存储登录返回的令牌
    修改Pinia的handleLogin方法,将返回的令牌存入本地存储:

    const handleLogin = async (credentials) => {
      await csrfCookie();
      const { data } = await login(credentials);
      localStorage.setItem('api_token', data.token);
      await fetchUser();
    };
    
  2. 配置Axios请求拦截器自动携带令牌
    修改Axios配置,添加请求拦截器,为每个请求自动带上Authorization头:

    axios.defaults.withCredentials = true;
    const api = axios.create({
        baseURL: import.meta.env.VITE_BASE_URL,
    })
    
    api.interceptors.request.use(function (config) {
      const token = localStorage.getItem('api_token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    }, function (error) {
      return Promise.reject(error);
    });
    
    export default api
    
  3. 登出时清除本地令牌
    修改Pinia的handleLogout方法,清除存储的令牌:

    const handleLogout = async () => {
      await logout();
      user.value = null;
      localStorage.removeItem('api_token');
    };
    

额外检查项

  • 确认VITE_BASE_URL环境变量值正确(如http://localhost:8000)
  • 清除浏览器缓存,避免旧Cookie或令牌干扰请求
  • 确认前端运行端口已包含在Sanctum的stateful配置中(你的配置已覆盖常用端口,无需修改)
  • 确认CORS配置的supports_credentials为true(你的配置已满足)

内容的提问来源于stack exchange,提问作者user22809416

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:36:04