这是 Laravel 11 + Fortify + Sanctum。我使用 Laravel 作为我的 API 后端。前端是第一方 SPA。
我刚刚使用 Thunder 客户端 (XHR) 测试我的登录端点 (POST)。当登录调用成功一次时,对登录端点的任何后续调用都会发出到根 URL 的重定向,而不是返回 JSON 响应,告诉用户已通过身份验证。这意味着调用者将得到 405(方法不允许)作为最终响应,因为根 url 上没有 POST 端点。
过去,我们可以通过修改
RedirectIfAuthenticated
中间件并仅在这不是 XHR 时进行重定向来控制这种错误行为。在 Laravel 11 中,他们已将这些中间件移至框架本身中。我还听说他们已经解决了问题本身,因此无需再编辑中间件,但我仍然看到该错误。
我还尝试了 Laravel 11 的新辅助方法来覆盖这样的重定向行为(在
bootstrap/app.php
中):
$middleware->redirectUsersTo(function() {
if(request()->wantsJson()) {
return response()->json(['result' => 'success'], 200);
} else {
return '/home';
}
});
但这会导致其他不相关的问题。我在这里错过了什么吗?
我发现将以下内容添加到
RedirectIfAuthenticated
中间件的 handle
函数可以解决问题:
if ($request->expectsJson())
return response()->json(['message' => 'authenticated.'], 200);
这个问题和这个修复似乎已经众所周知很多年了。然而,事实上,这个中间件现在是 Laravel 11 框架的一部分,并且位于
vendor
目录中,这意味着我必须手动将这一行添加到部署中,并注意它在更新期间不会被覆盖。不明白为什么 Laravel 在过去的几个主要版本中没有添加这个简单的检查。
// In app\Providers\AppServiceProvider.php
// In the boot function paste the code snippet below and change the route accordingly
use Illuminate\Auth\Middleware\RedirectIfAuthenticated;
// Redirect Authenticated Users
RedirectIfAuthenticated::redirectUsing(function () {
return route('dashboard');
});
找到解决方案。您需要在
home
中有一条名为 dashboard
或 api.php
的路线。定义此路由后,对 login
的任何后续调用都会正确返回 JSON 数据(由 home
路由返回),并且不会发生重定向。
正如问题中提到的,只有当用户已经登录(并且设置了 cookie)时才会发生重定向问题。未经身份验证的用户过去常常在第一次
login
调用时获得正确的 JSON 响应。添加 /home
路线可解决此问题。
看起来这些名称是硬编码在 Laravel 11 的中间件中的。由于中间件现在是框架的一部分,因此您无法在用户态代码中更改它。请注意,早些时候我也尝试过用我的自定义实现来覆盖默认中间件,但无法使其工作。
基于kris gjika的建议,您可以创建
app/Http/Middleware/RedirectIfAuthenticated.php
,扩展默认中间件,例如(https://onlinephp.io/c/ee199)。然后在 bootstrap/app.php 中使用它。
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
$middleware->alias(['json_guest'=> RedirectIfAuthenticated::class]);
})
有了这个,您可以在路由文件中将“guest”中间件替换为“json_guest”
我也面临这个问题,从 Laravel 4 到 10,这从来都不是问题,因为我们习惯于根据需要修改
RedirectIfAuthenticated
类。
这个问题的解决方案有点棘手,但可以通过创建一个自定义中间件来完成,该中间件扩展主 RedirectIfAuthenticated 类并覆盖句柄函数。我想知道为什么 Laravel 团队没有在内部实现这一点,因为 sainttum 对基于不记名令牌的请求有一个后备检查。
要让 Sanctum 发挥其功能,同时在发出 JSON 请求时修改 JSON 响应,请按以下方式完成:
// File: App/Http/Middleware/RedirectIfAuthenticated.php
use Illuminate\Http\JsonResponse;
use Illuminate\Auth\Middleware\RedirectIfAuthenticated as RedirectIfAuthenticatedMiddleware;
use Auth;
class RedirectIfAuthenticated extends RedirectIfAuthenticatedMiddleware
{
public function handle(Request $request, Closure $next, string ...$guards): Response|JsonResponse
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
if ($request->expectsJson()) {
return response()->json([
'message' => 'Authenticated users cannot access this resource.'
], 403);
}
return redirect($this->redirectTo($request));
}
}
return $next($request);
}
}
// File: bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->api(prepend: [
\Illuminate\Session\Middleware\StartSession::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
]);
$middleware->alias([
'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
]);
})
您可以使用应用程序 bootstrap/app.php 的方法redirectGuestsTo 来修改此行为: https://laravel.com/docs/11.x/authentication#redirecting-unauthenticated-users
<?php
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo('/login');
// Using a closure...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})
我无法直接回答对此线程的一些贡献,但以防万一,与更改
guest
别名类似,您可以在 $middleware->replace(...)
中使用 app.php
。
我尝试了所选答案中概述的方法,但没有成功。中间件将重定向到
/
而不是 /home
或 /dashboard
。
编辑:尝试登录新注册用户时仍然出现错误。
例如,您必须希望在会话结束时重定向页面
转到目录=> bootstrap > app.php
->withMiddleware(函数 (中间件 $middleware) {
$middleware->redirectGuestsTo(fn () => route('login'));
})
如果您有不止一名守卫
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo(function ($request) {
if (!$request->expectsJson()) {
if (in_array('auth:admin', $request->route()->middleware())) {
if (!auth('admin')->check()) {
return url('dashboard/admin/login');
}
}//end if if admin auth
}//end of if
});
})