我在NestJS应用程序中使用JwtService.JWT的初始化方式如下:

import { JwtModule } from '@nestjs/jwt';
// ...
JwtModule.register({
    secret: 'mysecret', // TODO: Change to production injected value.
    signOptions: { expiresIn: '1h' },
}),

我创建了一个JWT,并按如下方式签名:

import { JwtService, JwtSignOptions } from '@nestjs/jwt';

// ...

constructor(
    private readonly jwtService: JwtService,
) {}

// ...

const userInfo = {
    email: user.email,
    firstName: user.firstName,
    lastName: user.lastName
};
const accessTokenPayload = {
    iss: 'Auth Server 3.2',     // Issuer
    sub: user.id.toString(),    // Subject (user ID)
    aud: ['myClient'],          // Audience (recipient(s))
    exp: (new Date()).getTime() + 24 * 60 * 60 * 1000,  // Expiration time (Unix timestamp)
    nbf: (new Date()).getTime() - 60 * 60 * 1000,
    iat: new Date().getTime(),              // Issued at (Unix timestamp)
    jti: randomBytes(32).toString('hex'),   // JWT ID (unique identifier)
    data: { userInfo }
}
const accessToken = this.jwtService.signAsync(accessTokenPayload);

这将产生以下令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRoIFNlcnZlciAzLjIiLCJzdWIiOiIxIiwiYXVkIjpbIm15Q2xpZW50Il0sImV4cCI6MTY4MzE4Nzc4OTIzNSwibmJmIjoxNjgzMDk3Nzg5MjM1LCJpYXQiOjE2ODMxMDEzODkyMzUsImp0aSI6ImM5ZTIwY2E5ODAwNzEzNGUyNWFmNjk3MzZmZTIyOGM3MTQyNzBhMWQ1NWY5OGFjOWVjZTU2NmQxOTAyYWJiYzUiLCJkYXRhIjp7InVzZXJJbmZvIjp7ImVtYWlsIjoidXNlckBlbWFpbC5jb20iLCJmaXJzdE5hbWUiOiJKb2huIiwibGFzdE5hbWUiOiJTbWl0aCJ9fX0.WSJsnzsZNrMPOQLuPbDoeigFeB6IRJFmXo2qUSFCi3k

它具有以下有效负载:

{
  "iss": "Auth Server 3.2",
  "sub": "1",
  "aud": [
    "myClient"
  ],
  "exp": 1683187789235,
  "nbf": 1683097789235, // valid nbf value = 2023-05-03T07:09:49.235Z
  "iat": 1683101389235,
  "jti": "c9e20ca98007134e25af69736fe228c714270a1d55f98ac9ece566d1902abbc5",
  "data": {
    "userInfo": {
      "email": "user@email.com",
      "firstName": "John",
      "lastName": "Smith"
    }
  }
}

The Problem:(请仔细阅读,因为这不是重复的问题)

使用此代码验证JWT时:

try {
    await this.jwtService.verifyAsync(accessToken);
} catch(e) {
    console.log('Error', e)
}

我收到以下错误:

Error NotBeforeError {
        name: 'NotBeforeError',
        message: 'jwt not active',
        date: +055303-05-11T16:49:49.000Z  // --> note the invalid nbf value
}

55303,5月11日星期五格林尼治标准时间16:49:49‘

你知道我做错了什么吗?

PS:

  1. 注释NBF字段可以解决这个问题,但我无法验证令牌是否在特定日期之前未使用.
  2. 为NbF Select 其他值(例如nbf: 1683097789235等于2023-05-03T07:09:49.235Z)会产生相同的错误.

推荐答案

问题出在为签名方法提供的日期输入参数中:

exp: (new Date()).getTime() + 24 * 60 * 60 * 1000,  
nbf: (new Date()).getTime() - 60 * 60 * 1000,
iat: new Date().getTime(),

即使这些值(来自编码的JWT)在从它们创建新的Java脚本日期时转换为有效日期:

"exp": 1683187789235, // new Date(1683187789235) = 2023-05-04T08:09:49.235Z = valid date
"nbf": 1683097789235, // new Date(1683097789235) = 2023-05-03T07:09:49.235Z = valid date
"iat": 1683101389235, // new Date(1683101389235) = 2023-05-03T08:09:49.235Z = valid date

这些值是not valid JWT dates,因为JWT标准要求在seconds中使用Unix时间,而在JS中,Unix时间戳基于milliseconds.

因此,问题的答案是将提供给JWT信号函数(signAsync)的日期从毫秒转换为秒,如下所示:

const accessTokenPayload = {
    iss: 'Auth Server 3.2',     // Issuer
    sub: user.id.toString(),    // Subject (user ID)
    aud: ['myClient'],          // Audience (recipient(s))
    exp: Math.floor(((new Date()).getTime() + 24 * 60 * 60 * 1000) / 1000),  // Expiration time (Unix timestamp)
    nbf: Math.floor(((new Date()).getTime() - 60 * 60 * 1000) / 1000),
    iat: Math.floor(((new Date()).getTime()) / 1000),              // Issued at (Unix timestamp)
    jti: randomBytes(32).toString('hex'),   // JWT ID (unique identifier)
    data: { userInfo }
}

Note: To check the actual values of the a timestamp in a JWT, don't use new Date(valueFromJwt), but rather use https://jwt.io/ and hover over the time fileds to see how the jwt interprets the date: enter image description here

Javascript相关问答推荐

创建私有JS出口

如何通过onClick为一组按钮分配功能;

如何修复内容安全策略指令脚本-SRC自身错误?

react 路由加载程序行为

vanillajs-datepicker未设置输入值,日期单击时未触发更改事件

django无法解析余数:[0] from carray[0]'

在拖放时阻止文件打开

如何从URL获取令牌?

无法从NextJS组件传递函数作为参数'

在运行时使用Next JS App Router在服务器组件中运行自定义函数

TypeError:无法分解';React2.useContext(...)';的属性';basename';,因为它为空

查询参数未在我的Next.js应用路由接口中定义

AJAX POST在控制器中返回空(ASP.NET MVC)

使用类型:assets资源 /资源&时,webpack的配置对象&无效

expo 联系人:如果联系人的状态被拒绝,则请求访问联系人的权限

每次重新呈现时调用useState initialValue函数

为什么延迟在我的laravel项目中不起作用?

为什么我不能使用其同级元素调用和更新子元素?

我如何才能让p5.js在不使用实例模式的情况下工作?

CSS网格使页面自动滚动