Laravel+React Native使用Sanctum遇302重定向至首页问题
Laravel Sanctum 保护React Native API路由返回302重定向问题
问题场景
使用Laravel作为后端、React Native作为前端,通过Laravel Sanctum个人访问令牌保护API路由。当前流程:用户通过邮箱密码登录/注册后获取Sanctum令牌,存储到AsyncStorage,再通过Axios拦截器将令牌附加到所有请求头中。但受auth:sanctum中间件保护的路由返回302 Found状态码,随后被重定向至首页并返回200 OK。
现有配置代码
后端生成令牌代码
$token = $user->createToken($request['device_name'])->plainTextToken;
Axios添加Bearer令牌到请求头代码
if (token) { console.log('SANCTUM: Adding bearer token to axios: ' + token); axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; }
路由保护配置
Route::get('/auth/sanctum/user', 'App\Http\Controllers\Api\AuthController@sanctumUser')->middleware('auth:sanctum');
修改后的RedirectIfAuthenticated中间件
<?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null ...$guards * @return mixed */ //Added && !$request->wantsJson() part public function handle($request, Closure $next, ...$guards) { $guards = empty($guards) ? [null] : $guards; foreach ($guards as $guard) { //Added !$request->wantsJson() if (Auth::guard($guard)->check() /*&& !$request->expectsJson()*/ ) { //Tried changing this too return redirect(RouteServiceProvider::HOME); } } return $next($request); } }
Kernel.php中间件组配置
protected $middlewareGroups = [ 'web' => [ //\App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, //\Illuminate\Session\Middleware\StartSession::class, //\Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ //EnsureFrontendRequestsAreStateful::class, \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
sanctum.php配置文件
<?php return [ /* |-------------------------------------------------------------------------- | Stateful Domains |-------------------------------------------------------------------------- | | Requests from the following domains / hosts will receive stateful API | authentication cookies. Typically, these should include your local | and production domains which access your API via a frontend SPA. | */ 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1,127.0.0.1:8000,::1')), /* |-------------------------------------------------------------------------- | Expiration Minutes |-------------------------------------------------------------------------- | | This value controls the number of minutes until an issued token will be | considered expired. If this value is null, personal access tokens do | not expire. This won't tweak the lifetime of first-party sessions. | */ 'expiration' => null, /* |-------------------------------------------------------------------------- | Sanctum Middleware |-------------------------------------------------------------------------- | | When authenticating your first-party SPA with Sanctum you may need to | customize some of the middleware Sanctum uses while processing the | request. You may change the middleware listed below as required. | */ 'middleware' => [ 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, ], ];
auth.php配置文件
<?php return [ /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], /* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider. | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session", "token" | */ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false ], ], /* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that the reset token should be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], ], /* |-------------------------------------------------------------------------- | Password Confirmation Timeout |-------------------------------------------------------------------------- | | Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ 'password_timeout' => 10800, ];
请求头日志
Request Headers: {"Accept": "application/json", "Authorization": "Bearer rlRo0x0pz8ivKiveNEEAUqNL9K5hKxKdc2gdFi8lYhkMBMmCN7OzGgtv4Kex", "isPremiumActive": 0, "languageCode": "es", "lat": 36.7203234, "lng": -4.4062555}
解决方案
1. 确保API路由归属api中间件组
将受保护的路由放在api中间件组内,避免Web中间件的重定向逻辑干扰:
Route::middleware('api')->group(function () { Route::get('/auth/sanctum/user', 'App\Http\Controllers\Api\AuthController@sanctumUser')->middleware('auth:sanctum'); });
2. 修复RedirectIfAuthenticated中间件逻辑
当请求为API请求(期望JSON响应)时,避免重定向,直接放行或返回JSON响应:
public function handle($request, Closure $next, ...$guards) { $guards = empty($guards) ? [null] : $guards; foreach ($guards as $guard) { if (Auth::guard($guard)->check()) { // 针对API请求返回JSON而非重定向 if ($request->expectsJson()) { return response()->json(['message' => '已认证'], 200); } return redirect(RouteServiceProvider::HOME); } } return $next($request); }
3. 配置Sanctum认证Guard
在config/auth.php中修改API Guard驱动为sanctum,确保使用Sanctum的令牌验证逻辑:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'sanctum', // 替换原token驱动为sanctum 'provider' => 'users', ], ],
4. 确认User模型使用Sanctum Trait
确保App\Models\User模型引入HasApiTokens trait,否则无法生成和验证个人访问令牌:
use Laravel\Sanctum\HasApiTokens; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; // ... 其他模型代码 }
5. 验证令牌有效性
可以通过以下方式排查令牌问题:
- 检查数据库
personal_access_tokens表,确认生成的令牌与请求中的令牌一致,且未过期 - 在控制器中手动验证令牌:
use Illuminate\Http\Request; public function sanctumUser(Request $request) { $token = $request->bearerToken(); $validToken = \Laravel\Sanctum\PersonalAccessToken::findToken($token); if (!$validToken || $validToken->expired()) { return response()->json(['message' => '无效令牌'], 401); } return response()->json($request->user()); }
内容的提问来源于stack exchange,提问作者gabogabans
相关产品推荐
相关产品推荐

