asp.net 服务器生成的 JWT 令牌未被给定相同秘密密钥的 nodejs 服务器读取
JWT token generated by asp.net server not being read by nodejs server given the same SECRET key
我们有 ASP.NET WEB API 服务器框为经过身份验证的用户生成 JWT 令牌。服务器正在使用一个秘密:SECRET。这些令牌被发送回我们的 UI,所有 API 调用都带有此令牌。
最近我们开始在 nestjs 中编写我们的新服务,并使用@nestjs/passport、passport-jwt 库进行授权。我们的 UI 将开始点击这些新的 API,因此我们希望通过我们的 ASP.NET WEB API 服务器生成的相同令牌来授权我们的用户。我认为这将是直截了当的,因为我只需要使用相同的秘密来解码令牌:SECRET。但是我在设置jwt策略后一直从nestjs收到401。
尝试使用相同的 SECRET 从 nestjs 创建令牌并使用它,并且成功了。但是 ASP.NET 服务器生成的令牌不起作用。
来自 ASP.NET WEB API 创建令牌的方法:
(注意:密钥 = 秘密)
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 = 秘密)
@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 中创建令牌的代码是错误的,因为您只使用了 base64 encoding
。
结果是包含保留字符的标记 (+
/
=
)
JWT 使用 base64Url encoding
(参见 https://www.rfc-editor.org/rfc/rfc7519#section-3), which avoids these characters. These are are reserved characters for URIs (see section 2.2. in https://www.ietf.org/rfc/rfc2396.txt)并且在 JWT 中是不允许的,因为有时令牌作为参数在 url.
中传输
推荐的解决方案是使用 one of the JWT libraries。在您的代码中,您手动执行操作(这有利于学习目的),因此要继续这种方式,您需要将 base64
编码结果转换为 base64url
encoding.
这可以用string.Replace
来完成:
string payloadEncoded = UTF8Bas64Encode(payload).TrimEnd('=').Replace('+', '-').Replace('/', '_');
header 和签名当然也一样!
这是正确的。我设法使用 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
class 并且它有效。这个 class 根据规范进行编码和必要的字符替换,所以我会推荐其他人使用它。到目前为止,我们的 JWT 令牌并未用于我们系统之外的声明,因此该问题没有引起注意。
我们有 ASP.NET WEB API 服务器框为经过身份验证的用户生成 JWT 令牌。服务器正在使用一个秘密:SECRET。这些令牌被发送回我们的 UI,所有 API 调用都带有此令牌。
最近我们开始在 nestjs 中编写我们的新服务,并使用@nestjs/passport、passport-jwt 库进行授权。我们的 UI 将开始点击这些新的 API,因此我们希望通过我们的 ASP.NET WEB API 服务器生成的相同令牌来授权我们的用户。我认为这将是直截了当的,因为我只需要使用相同的秘密来解码令牌:SECRET。但是我在设置jwt策略后一直从nestjs收到401。
尝试使用相同的 SECRET 从 nestjs 创建令牌并使用它,并且成功了。但是 ASP.NET 服务器生成的令牌不起作用。
来自 ASP.NET WEB API 创建令牌的方法: (注意:密钥 = 秘密)
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 = 秘密)
@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 中创建令牌的代码是错误的,因为您只使用了 base64 encoding
。
结果是包含保留字符的标记 (+
/
=
)
JWT 使用 base64Url encoding
(参见 https://www.rfc-editor.org/rfc/rfc7519#section-3), which avoids these characters. These are are reserved characters for URIs (see section 2.2. in https://www.ietf.org/rfc/rfc2396.txt)并且在 JWT 中是不允许的,因为有时令牌作为参数在 url.
推荐的解决方案是使用 one of the JWT libraries。在您的代码中,您手动执行操作(这有利于学习目的),因此要继续这种方式,您需要将 base64
编码结果转换为 base64url
encoding.
这可以用string.Replace
来完成:
string payloadEncoded = UTF8Bas64Encode(payload).TrimEnd('=').Replace('+', '-').Replace('/', '_');
header 和签名当然也一样!
这是正确的。我设法使用 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
class 并且它有效。这个 class 根据规范进行编码和必要的字符替换,所以我会推荐其他人使用它。到目前为止,我们的 JWT 令牌并未用于我们系统之外的声明,因此该问题没有引起注意。