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

Laravel接口测试遇Route[login]未定义及认证失败问题求助

Laravel JSON API 认证问题排查与解决

问题现象

  1. 对API端点发起GET请求时,抛出错误:
Symfony\Component\Routing\Exception\RouteNotFoundException: Route [login] not defined
  1. 创建自定义认证中间件后,携带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');
    }
}

问题原因分析

  1. Route [login] 未定义:默认Authenticate中间件在未认证时会尝试跳转至login路由。若请求未正确设置Accept: application/json头,expectsJson()返回false,就会调用route('login'),但项目未定义该路由,导致报错。
  2. 自定义中间件未处理api_token验证:CheckApiToken仅检查Accept头格式,完全未验证api_token有效性,因此即使携带api_token也无法完成认证。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:50:35