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

JWT登录时加密用户ID致AccessToken无效的修复求助

问题分析

你遇到的核心矛盾在于:

  1. User模型通过getIdAttribute返回解密后的真实ID,但数据库存储的是加密后的ID字符串
  2. JWT默认使用模型的getKey()方法获取用户标识(sub字段),原逻辑下getKey()会返回解密后的ID,导致Token验证时,用真实ID去匹配数据库里的加密ID,自然查不到用户,Token失效
  3. 同时需要保证boot函数中创建/更新者ID的存储与关联查询正常
解决方案

1. 修改User模型,分离「对外展示的真实ID」和「数据库存储的加密ID」

重写getKey()方法让JWT使用加密后的ID生成Token,同时保留getIdAttribute让前端获取解密后的真实ID:

use Illuminate\Support\Facades\Crypt;

class User extends Model
{
    protected $fillable = ['name', 'email', 'password'];
    // 因为主键改为加密字符串,需调整类型配置
    protected $keyType = 'string';
    public $incrementing = false;

    // 加密ID后存入数据库
    public function setIdAttribute($value)
    {
        $this->attributes['id'] = Crypt::encrypt($value);
    }

    // 取出时解密,给前端返回真实ID
    public function getIdAttribute($value)
    {
        return Crypt::decrypt($value);
    }

    // 重写getKey,让JWT使用数据库中存储的加密ID
    public function getKey()
    {
        return $this->attributes['id'];
    }

    // 可选:提供直接获取加密ID的方法,方便业务使用
    public function getEncryptedIdAttribute()
    {
        return $this->attributes['id'];
    }
}

2. 修复登录函数,生成有效的JWT Token

补充Token生成逻辑,此时Token的sub字段是加密后的ID,验证时能匹配数据库存储:

public function login(Request $request)
{
    $credentials = $request->only('email', 'password');

    if (Auth::attempt($credentials)) {
        $user = Auth::user();
        // 生成有效JWT Token
        $token = $user->createToken('AccessToken')->accessToken;
        $decryptedId = $user->id;
        $name = $user->name;
        $email = $user->email;

        return response()->json([
            'user_id' => $decryptedId,
            'name' => $name,
            'email' => $email,
            'access_token' => $token,
            'token_type' => 'Bearer',
        ]);
    } else {
        return redirect()->back()->withErrors(['login' => 'Invalid credentials']);
    }
}

3. 保证boot函数中创建/更新者功能正常

在模型boot逻辑中,使用getKey()获取加密后的ID存入created_by/updated_by,确保关联查询匹配:

protected static function boot()
{
    parent::boot();

    static::creating(function ($model) {
        if (Auth::check()) {
            // 存入加密后的用户ID,与数据库存储的用户主键匹配
            $model->created_by = Auth::user()->getKey();
        }
    });

    static::updating(function ($model) {
        if (Auth::check()) {
            $model->updated_by = Auth::user()->getKey();
        }
    });
}

4. 数据库表结构适配

如果原用户表的id是自增整数,需修改为字符串类型(确保长度足够容纳加密后的内容):

// 迁移文件示例
Schema::create('users', function (Blueprint $table) {
    $table->string('id')->primary(); // 替代原$table->id()
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});
关键逻辑说明
  • JWT生成时用加密ID作为用户标识,验证时直接匹配数据库存储的加密ID,解决Token无效问题
  • 模型对外暴露的id属性始终是解密后的真实ID,不影响前端业务使用
  • 创建/更新者ID存储加密值,关联查询时自动匹配用户表主键,保证关联功能正常

内容的提问来源于stack exchange,提问作者AlaN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:57:49