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



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

以下是身份验证服务的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不验证公钥?

因为已经好几天了,我猜这个问题已经解决了。我只是在这里加上我的两美分给未来的读者。

问题在于JwtModule和JwtStrategy实例化。它们的配置不正确。您需要传入用于签名和验证令牌的算法以及密钥。要验证令牌是否真的是用RS256算法生成的,请在https://jwt.io/.它可能会显示HS256,并且由于您的代码没有使用正确的算法对令牌进行签名。当使用公钥验证令牌时,它会失败。

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

  • 您需要在signOptions中添加算法作为RS256,并在JwtModule配置中传入公钥私钥
  • 然后,在您的服务中,您将在签名时使用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将提供env形式的RSA密钥对。强烈建议不要签入代码中的键。

最新更新