Laravel Sanctum出现401未授权错误

24

我正在使用 Laravel Sanctum 和 Vuejs SPA。两者都驻留在同一顶级域中。

Laravel backend : app.demo.localhost
Vue SPA : app-spa.demo.localhost

从VueJS SPA使用axios调用登录和注销(endpoints)时,成功设置了XSRF-TOKEN,并且工作正常。但是当我调用其他api endpoints时,它会返回401未经授权的错误。

在axios中进行了如下设置

axios.defaults.withCredentials = true;

我有以下配置

在 Laravel .env 文件中

SESSION_DRIVER=cookie
SESSION_DOMAIN=.demo.localhost
SANCTUM_STATEFUL_DOMAINS=app-spa.demo.localhost
在Routes/Api.php中。
Route::middleware('auth:sanctum')->get('api/user', function (Request $request) {
   return $request->user();
});

在 cors.php 中

'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout'],

'allowed_methods' => ['*'],

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => [],

'max_age' => 0,

'supports_credentials' => true,

有人可以帮我吗?


1
以上内容中没有显示出来,你是否已经在 app/Http/Kernel.php 中启用了 Sanctum 中间件呢? https://laravel.com/docs/7.x/sanctum#spa-authentication - Dylan Hobbs
你解决了这个问题吗? - pu4cu
我在生产服务器上遇到了同样的问题,使用React和Axios。在开发服务器上它可以正常工作。 - Develope Cruz
有人解决了吗?我使用自定义守卫成功登录,但是后续请求失败,即使设置了xsrf令牌等... - Eden WebStudio
面对同样的问题,请提供答案,如果有人已经找到了。 - Rukmi Patel
10个回答

38
如果您正在使用 `php artisan serve`,请将端口号添加到 `SANCTUM_STATEFUL_DOMAINS` 中。因此,如果您的端口号是8000:
SESSION_DRIVER=cookie
SESSION_DOMAIN=.demo.localhost
SANCTUM_STATEFUL_DOMAINS=app-spa.demo.localhost:8000

您的SANCTUM_STATEFUL_DOMAINS必须与您浏览器中的URL匹配。端口号不应该出现在SESSION_DOMAIN上。


我在SESSION_DOMAIN设置中添加了端口号,导致返回401错误 - 我移除了端口号后,它立即正常工作 - 谢谢! - martinethyl
1
默认情况下,APP_URL 是有状态的。在我的 .env 文件中修复了 APP_URL 后,问题得到了解决。谢谢! - Buraco

16
以下是我在设置Laravel sanctum时遵循的8个步骤,如果您错过了任何步骤,请检查一下: 步骤1 composer require laravel/sanctum 步骤2 php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider 步骤3 运行 php artisan migrate (如果您使用的是spa,可以忽略此步骤) 步骤4 取消注释app/http/kernel.php中的以下行:\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 步骤5 在config/cors.php中更新:'supports_credentials' => true, 步骤6 在.env文件中更新: SESSION_DRIVER=cookie, 并添加一行SESSION_DOMAIN=localhost(即使您使用8080端口也要在session_domain中提到localhost) 步骤7 在 config/sanctum.php 中将您的客户端域名和端口(如果是本地)添加到stateful中,例如:对于vue CLI通常为localhost:8080,对于nuxt则为localhost:3000,代码如下:
    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:8000,localhost:8080,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

大多数情况下,如果您的有状态(Step7)未正确设置,则会收到401未经授权的错误或尝试将您重定向到主页并出现CORS策略错误。

Step8 不要忘记等待直到sanctum/csrf-cookie承诺被解决。

    async login() {
      await axios.get("http://localhost:8000/sanctum/csrf-cookie");

      await axios.post("http://localhost:8000/login", {
        email: "kunal@gmail.com",
        password: "password",
      });

      let response = await axios.get("http://localhost:8000/api/user");
      console.log(response.data);
    },


感谢@Kanul Rajput提供这个全面的答案。在我进行此调用之前,我的登录是在守卫_before_中发生的,我认为这可能会给我带来问题。您知道是否有任何方法可以“手动”触发使用XSRF令牌调用时发生的任何过程吗? - theotherdy
非常感谢,这在我的情况下是EnsureFrontendRequestsAreStateful。 - Zack Heisenberg
第七步对我来说是个障碍。我设置了 SANCTUM_STATEFUL_DOMAINS=http://localhost:3000,只需要移除 http:// 即可。你只需要使用 SANCTUM_STATEFUL_DOMAINS=localhost:3000 即可。 - Joseph Ajibodu
(对于多个守卫)第8步可以是在config/sanctum.php中将'guard' => ['web', 'admin']添加额外的守卫。 - Kaustubh Bagwe

9

对于任何处理本地主机的人:

SESSION_DRIVER=cookie    
SESSION_DOMAIN=localhost    
SANCTUM_STATEFUL_DOMAINS=localhost:8080(port number you use)

4

对于我来说,我只需要将主机和端口号放在一起:

SANCTUM_STATEFUL_DOMAINS=127.0.0.1:5173

然后它就开始工作了。也许这可以帮助某些人。


1
我刚遇到了同样的问题。我按照官方文档配置了所有选项,但无法获得授权。
然后我使用了routes/web.php而不是routes/api.php,这样我就可以很好地使用sanctum中间件。
现在问题似乎很明显,Axios withCredentials可能需要放在正确的位置。
const http = axios.create({
    baseURL: API_URL,
    withCredentials: true
})

也许不起作用。所以我加上了{withCredentials: true},像这样

http.get('/api/whoami', {withCredentials: true})
  .then(res => {
                console.log(res.data)
            })

然后它就正常工作了。

但非常奇怪的是,现在它已经正常了,无论我清除浏览器缓存、cookie还是Laravel的各种缓存,都没有之前的情况了。


人类...那是唯一对我有效的解决方案!谢谢。 - Mert Metin

0
我的问题是我在错误的位置设置了域名。 我以为它是一个域名数组,在config/sanctum.php中,但实际上不是,需要放在字符串内部:

好的:

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1,myownlocaldomain.test,myownlocaldomain.test:8080', <-------- OK
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

不好:

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '',
        'myownlocaldomain.test', <----- BAD
        'myownlocaldomain.test:8080', <---- BAD
    ))),

我希望能为其他人节省数天的工作时间...


0

嗨,我找到了一个解决方案。

我的SPA是Vue v3,运行在3000端口上。 同时,我的后端是在80端口上运行的。(laravel 8.1)

在config/sanctum.php中创建有状态域,就像这样

 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost:3000',
    env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
))),

只在正确的域名上添加一个,神奇地为我解决了问题。之前我写了所有可能的端口变体,这让我疯狂了几天几夜。


0

我的问题是....(没有认真阅读)

如果您的SPA需要使用私有/存在广播频道进行身份验证,您应该将 Broadcast::routes 方法调用放在 您的 routes/api.php 文件中


0

使用Laravel 10、VueJs 3和Sanctum包。我通过在.env文件中设置APP_URL=[我的域名]并将值与Web浏览器中的域名相匹配来解决了这个问题。

你可以试一下,希望能帮到你。


0
你的SPA应用如果在不同的端口上,就不再是一个一方应用,而只是在你的开发环境中充当一个移动应用程序。
例如,
Laravel API
localhost:8000

Vuejs应用程序
locahost:5781

所以在本地开发环境中,当你需要进行身份验证时,你需要通过发送授权头来进行验证。 注意:这只适用于你的本地开发环境。因此,请将下面的代码放在一个if语句中。
axios.interceptors.request.use(
  config => {
    // Do something before request is sent
    
    if(process.env.dev) {
       // accessToken was set after a successful login
       const accessToken = localStorage.getItem('accessToken')
    
       if (accessToken) config.headers.Authorization = `Bearer ${accessToken}`
    }
    

    return config
  },
error => Promise.reject(error),
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接