NestJs 使用 jwt 和私钥和公钥进行身份验证

Posted

技术标签:

【中文标题】NestJs 使用 jwt 和私钥和公钥进行身份验证【英文标题】:NestJs authentication using jwt and private and public key 【发布时间】:2020-08-19 17:34:56 【问题描述】:

我正在尝试使用nestJS 了解jwt 和身份验证。 我创建了两个独立的微服务,其中一个是身份验证服务,成功登录后,客户端获取 jwt 令牌,并使用此令牌可以访问另一个微服务。

这里是auth服务的JwtStrategy和AuthModule的代码:

import  ExtractJwt, Strategy  from 'passport-jwt';
import  PassportStrategy  from '@nestjs/passport';
import  Injectable  from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 
  constructor() 
    super(
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secretKey'
    );
  

  async validate(payload: any) 
    return payload;
  

import  Module  from '@nestjs/common';
import  AuthService  from './auth.service';
import  UsersModule  from '../users/users.module';
import  PassportModule  from '@nestjs/passport';
import  LocalStrategy  from './local.strategy';
import  JwtStrategy  from './jwt.strategy';
import  JwtModule  from '@nestjs/jwt';
import  jwtConstants  from './constants';
import  AuthController  from './auth.controller';
import * as fs from 'fs';

@Module(
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register(
      secret: 'secretKey',
      signOptions:  expiresIn: '1h' ,
    ),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
  controllers: [AuthController],
)
export class AuthModule 

这是另一个服务的代码:

import  ExtractJwt, Strategy  from 'passport-jwt';
import  PassportStrategy  from '@nestjs/passport';
import  Injectable  from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 
  constructor() 
    super(
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'secretKey',
    );
  

  async validate(payload: any) 
    return payload;
  

我发现对两个服务使用相同的密钥是没有意义的(因为如果我要创建 10 个微服务,我不会对所有微服务使用相同的密钥)所以我创建了一个私有和公共使用 openssl 的密钥。 在 AuthModule 中,我复制了私钥而不是“secretKey”字符串,在其他服务中,我复制了公钥而不是“secretKey”字符串,但我收到了 401,未经授权的错误。 我在这里错过了什么?为什么 JwtStrategy 不验证公钥?

【问题讨论】:

【参考方案1】:

已经好几天了,我猜这已经解决了。我只是在这里为未来的读者添加我的两分钱。

问题在于 JwtModule 和 JwtStrategy 实例化。它们配置不正确。您需要传入用于签名和验证令牌的算法以及密钥。要验证令牌是否实际上是使用 RS256 算法生成的,请检查令牌中的标头 https://jwt.io/。它可能会显示 HS256,因为您的代码没有使用正确的算法来签署令牌。并且在使用公钥验证令牌时失败。

使用 RSA 密钥对正确生成签名令牌:

您需要在signOptions中添加算法为RS256,并在JwtModule配置中传入publicprivate键。李> 然后在您的服务中,您将在签名时使用 PRIVATE_KEY 生成令牌。 JwtStrategy 用作警卫。它所做的只是根据配置验证 JWT。它期望对称密钥“秘密”或非对称密钥的“公共部分”进行验证。我们必须使用 PUBLIC_KEY。您还必须在此处指定要检查验证的算法。我们也必须在这里使用 RS256,因为我们使用它来生成令牌。

身份验证模块

@Module(
  imports: [
    ConfigModule,
    JwtModule.registerAsync(
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => 
        const options: JwtModuleOptions = 
          privateKey: configService.get('JWT_PRIVATE_KEY'),
          publicKey: configService.get('JWT_PUB LIC_KEY'),
          signOptions: 
            expiresIn: '3h',
            issuer: '<Your Auth Service here>',
            algorithm: 'RS256',
          ,
        ;
        return options;
      ,
      inject: [ConfigService],
    ),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
  controllers: [AuthController],
)
export class AuthModule 

身份验证服务

@Injectable()
export class AuthService 
  constructor(
    private jwtService: JwtService,
  ) 

  async generateToken(
    user: User,
    signOptions: jwt.SignOptions = ,
  ): Promise<AuthJwtToken> 
    const payload =  sub: user.id, email: user.email, scopes: user.roles ;
    return 
      accessToken: this.jwtService.sign(payload, signOptions),
    ;
  

JwtStrategy

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 
  constructor(private configService: ConfigService) 
    super(
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get('JWT_PUBLIC_KEY'),
      algorithms: ['RS256'],
    );
  

  async validate(payload: any) 
    const  sub: userId, email, scopes: roles  = payload;
    return 
      id: userId,
      email,
      roles,
    ;
  

在您的其他微服务中,您可以使用我们在 Auth 模块中使用的相同 JwtStrategy。

由于您正在创建分布式应用程序,因此您需要通过手动添加密钥或使用某些 API 端点公开密钥来与其他微服务共享 PUBLIC_KEY。无论哪种方式,您都必须使用 PUBLIC_KEY 以供其他服务进行验证。您不得分享或公开 PRIVATE_KEY

注意:以下代码假定一个 ConfigService,它将提供 RSA 密钥对形式的 env。强烈建议不要检查代码中的键。

【讨论】:

环境变量JWT_PRIVATE_KEY和JWT_PUB LIC_KEY是private.pem和public.pem文件所在路径的字符串? 嘿@JuanPabloMorenoMartín,变量是 pem 或 crt 文件的实际数据/字符串。如果您检查文档中的配置,github.com/nestjs/jwt#secret--encryption-key-options,它希望它们是实际值。您可能需要读取文件并在 configService 中返回数据或导出 env 中 pem 文件中的值 @AvikSarkar 如何通过 HTTP 调用检索公钥(通常,身份验证服务器通过 JWKS 提供公钥,如 AUTHSERVER/.well-known/jwks.json)?

以上是关于NestJs 使用 jwt 和私钥和公钥进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

请问SSL加密的公钥和私钥区别

公钥私钥和证书

android rsa加解密私钥和公钥怎么用

理解两种加密方式中私钥和公钥的概念

如果有人得到我的私钥和公钥,JWT 会发生啥?

java中RSA用私钥加密公钥解密问题