【问题标题】:JWT token generated by asp.net server not being read by nodejs server given the same SECRET key给定相同的 SECRET 密钥,由 asp.net 服务器生成的 JWT 令牌未被 nodejs 服务器读取
【发布时间】:2019-07-29 00:51:31
【问题描述】:

我们有 ASP.NET WEB API 服务器框为经过身份验证的用户生成 JWT 令牌。服务器正在使用一个秘密:SECRET。这些令牌被发送回我们的 UI,所有的 API 调用都带有这个令牌。

最近我们开始在nestjs 中编写我们的新服务,并使用@nestjs/passport、passport-jwt 库进行授权。我们的 UI 将开始使用这些新的 API,因此我们希望通过我们的 ASP.NET WEB API 服务器生成的相同令牌来授权我们的用户。我认为这将是直截了当的,因为我只需要使用相同的秘密解码令牌:秘密。但是在设置jwt策略后,我不断从nestjs得到401。

尝试使用相同的 SECRET 从 nestjs 创建一个令牌并使用它,并且成功了。但是 ASP.NET 服务器生成的令牌不起作用。

来自 ASP.NET WEB API 的创建令牌的方法: (注意:key = SECRET)

public string CreateToken(string payload) {                       
    var encoding = new System.Text.ASCIIEncoding();

    string unsignedToken =_header + tokenSeperater + payload;
    string signature;

    using (var hmacsha256 = new HMACSHA256(encoding.GetBytes(key))) {
        byte[] hashmessage = hmacsha256.ComputeHash(encoding.GetBytes(unsignedToken));
        signature=Convert.ToBase64String(hashmessage);
     }
     string payloadEncoded = UTF8Bas64Encode(payload);
     string token = "Bearer " + _headerEncode + tokenSeperater  + payloadEncoded + tokenSeperater  + signature;

     return token;
}

NestJS 护照设置: (注:jwtConfig.secret = SECRET)

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      publicKey: jwtConfig.secret,
      signOptions: {
        expiresIn: jwtConfig.expiresIn,
      },
    })
  ],
})
export class AuthModule {}

策略:

export class JwtStrategy extends PassportStrategy(Strategy) {

  constructor(
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: config.get('jwt.secret')
    });
  }

  async validate(payload: JwtPayload) {

    const user = <find user>;
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

这是来自我们的身份验证服务器的令牌:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiAiNDE3MSIsIlNlc3Npb25JZCI6ICI2Yjg2M2I2NS02YTM1LTQ1NDQtOTllNy0yMmJjMzEzY2M1YTMifQ==.bu+GZ+tq+Wc1Srvp/0u7jMvqBnDJqd2N5xWx2dItxlA=

预计会解密令牌,但在调用生命周期的早期就会收到 401。

【问题讨论】:

    标签: asp.net jwt nestjs passport-jwt


    【解决方案1】:

    这是正确的。我设法使用 NestJS 内部使用的 jws 模块调试令牌,并发现 jws 库中的以下行失败:

    var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/;
    
    function isValidJws(string) {
      return JWS_REGEX.test(string) && !!headerFromJWS(string);
    }
    
    

    进一步查看令牌时,我发现不在 Base64 列表中的字符 (https://en.wikipedia.org/wiki/Base64)。

    对于 C#,我切换到 System.IdentityModel.Tokens.Jwt 类并且它有效。这个类根据规范对字符进行编码和必要的替换,所以我会推荐其他人使用它。到目前为止,我们的 JWT 令牌还没有用于我们系统之外的声明,因此问题没有引起注意。

    【讨论】:

      【解决方案2】:

      您在 ASP.NET 中创建令牌的代码是错误的,因为您只使用了base64 encoding。 结果是一个包含保留字符的令牌(+/=

      JWT 使用base64Url encoding(请参阅https://www.rfc-editor.org/rfc/rfc7519#section-3),它避免了这些字符。这些是 URI 的保留字符(请参阅https://www.ietf.org/rfc/rfc2396.txt 中的第 2.2 节),并且不允许在 JWT 中使用,因为有时令牌会作为 url 中的参数传输。

      推荐的解决方案是使用one of the JWT libraries。在您的代码中,您是手动执行操作(这有利于学习目的),因此要继续这种方式,您需要将 base64 编码结果转换为 base64urlencoding。

      这可以通过string.Replace来完成:

      string payloadEncoded = UTF8Bas64Encode(payload).TrimEnd('=').Replace('+', '-').Replace('/', '_');
      

      标题和签名当然也一样!

      【讨论】:

        猜你喜欢
        • 2017-09-07
        • 2012-07-24
        • 1970-01-01
        • 2016-06-09
        • 1970-01-01
        • 2016-11-22
        • 2019-10-23
        • 2020-05-11
        • 1970-01-01
        相关资源
        最近更新 更多