Laravel接口测试遇Route[login]未定义及认证失败问题求助
Laravel JSON API 认证问题排查与解决
问题现象
- 对API端点发起GET请求时,抛出错误:
Symfony\Component\Routing\Exception\RouteNotFoundException: Route [login] not defined
- 创建自定义认证中间件后,携带
api_token发起请求(如http://127.0.0.1:8000/api/v1/tasks?api_token=***),仍返回:
{"message":"Unauthenticated."}
相关代码
自定义中间件 CheckApiToken
class CheckApiToken { public function handle($request, Closure $next) { if(!in_array($request->headers->get('accept'), ['application/json', 'Application/Json'])) return response()->json(['message' => 'Unauthenticated.'], 401); return $next($request); } }
kernel.php 的 api 中间件配置
'api' => [ \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\CheckApiToken::class, ],
Task 模型
class Task extends Model { use HasFactory; protected $fillable = ["title"]; public function creator() { return $this->belongsTo(User::class); } public function assignees() { return $this->belongsToMany(User::class); } }
路由代码
Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); JsonApiRoute::server('v1') ->prefix('v1') ->namespace('App\Http\Controllers\Api\V1') ->resources(function ($server) { $server->resource('users') ->parameter('id') ->relationships(function ($relationships) { $relationships->hasMany('tasks'); }); $server->resource('tasks') ->parameter('id') ->relationships(function ($relationships) { $relationships->hasMany('assignees'); $relationships->hasOne('creator'); }); });
Authenticate 中间件
class Authenticate extends Middleware { protected function redirectTo(Request $request): ?string { return $request->expectsJson() ? null : route('login'); } }
问题原因分析
- Route [login] 未定义:默认
Authenticate中间件在未认证时会尝试跳转至login路由。若请求未正确设置Accept: application/json头,expectsJson()返回false,就会调用route('login'),但项目未定义该路由,导致报错。 - 自定义中间件未处理
api_token验证:CheckApiToken仅检查Accept头格式,完全未验证api_token有效性,因此即使携带api_token也无法完成认证。 - JSON:API路由认证逻辑不匹配:当前路由未显式绑定有效认证中间件,或使用的
auth:sanctum与自定义api_token逻辑不兼容。
解决方法
1. 修复Route [login] 错误
修改Authenticate中间件,针对API路由直接返回null,避免跳转:
class Authenticate extends Middleware { protected function redirectTo(Request $request): ?string { // 对所有API路径直接返回null,不跳转登录页 if ($request->is('api/*')) { return null; } return $request->expectsJson() ? null : route('login'); } }
或确保所有API请求都携带Accept: application/json请求头,让expectsJson()返回true。
2. 完善api_token认证逻辑(传统方式)
步骤1:添加数据库字段
在users表中添加api_token字段(字符串类型,添加唯一索引):
ALTER TABLE users ADD COLUMN api_token VARCHAR(255) UNIQUE;
步骤2:修改CheckApiToken中间件
添加api_token验证逻辑:
use Illuminate\Support\Facades\Auth; class CheckApiToken { public function handle($request, Closure $next) { // 统一转换为小写,避免大小写问题 $acceptHeader = strtolower($request->headers->get('accept', '')); if ($acceptHeader !== 'application/json') { return response()->json(['message' => 'Unauthenticated.'], 401); } // 获取api_token,支持URL参数或Authorization头(Bearer格式) $apiToken = $request->query('api_token') ?? $request->header('Authorization'); if (!$apiToken) { return response()->json(['message' => 'Unauthenticated.'], 401); } // 处理Bearer token前缀 if (str_starts_with($apiToken, 'Bearer ')) { $apiToken = substr($apiToken, 7); } // 查找对应用户 $user = \App\Models\User::where('api_token', $apiToken)->first(); if (!$user) { return response()->json(['message' => 'Unauthenticated.'], 401); } // 设置认证用户 Auth::login($user); return $next($request); } }
步骤3:确保路由应用api中间件
在JSON:API路由中显式指定api中间件:
JsonApiRoute::server('v1') ->prefix('v1') ->namespace('App\Http\Controllers\Api\V1') ->middleware('api') ->resources(function ($server) { // ... 资源定义 });
3. 切换到Laravel Sanctum认证(推荐)
使用Sanctum无需自定义api_token逻辑,步骤如下:
步骤1:更新User模型
添加HasApiTokens trait:
use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; // ... 其他代码 }
步骤2:给路由添加auth:sanctum中间件
JsonApiRoute::server('v1') ->prefix('v1') ->namespace('App\Http\Controllers\Api\V1') ->middleware(['api', 'auth:sanctum']) ->resources(function ($server) { // ... 资源定义 });
步骤3:使用Sanctum Token请求
通过请求头携带Token:
Authorization: Bearer {sanctum_token}
或通过token查询参数(仅测试环境使用):
http://127.0.0.1:8000/api/v1/tasks?token={sanctum_token}
内容的提问来源于stack exchange,提问作者Emm
相关产品推荐
相关产品推荐

