Nest.js Auth Guard JWT 身份验证不断returns 401 未授权

Nest.js Auth Guard JWT Authentication constantly returns 401 unauthorized

使用 Postman 测试我的端点,我能够成功“登录”并收到 JWT 令牌。现在,我正在尝试访问一个据称具有 AuthGuard 的端点,以确保现在我已登录,现在可以访问它。

但是,即使在 Postman 中显示 JWT 令牌,它也会不断 returns 401 Unauthorized

这是我的代码:

user.controller.ts

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @UseGuards(AuthGuard())
    @Get()
    getUsers() {
        return this.usersService.getUsersAsync();
    }
}

jwt.strategy.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(
        private readonly authenticationService: AuthenticationService,
    ) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: 'SuperSecretJWTKey',
        });
    }

    async validate(payload: any, done: Function) {
        console.log("I AM HERE"); // this never gets called.
        const user = await this.authenticationService.validateUserToken(payload);

        if (!user) {
            return done(new UnauthorizedException(), false);
        }

        done(null, user);
    }
}

我也试过ExtractJWT.fromAuthHeaderWithScheme('JWT'),但还是不行。

authentication.module.ts

@Module({
    imports: [
        ConfigModule,
        UsersModule,
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secret: 'SuperSecretJWTKey',
            signOptions: { expiresIn: 3600 },
        }),
    ],
    controllers: [AuthenticationController],
    providers: [AuthenticationService, LocalStrategy, JwtStrategy],
    exports: [AuthenticationService, LocalStrategy, JwtStrategy],
})
export class AuthenticationModule {}

authentication.controller.ts

@Controller('auth')
export class AuthenticationController {
    constructor(
        private readonly authenticationService: AuthenticationService,
        private readonly usersService: UsersService,
    ) {}

    @UseGuards(AuthGuard('local'))
    @Post('login')
    public async loginAsync(@Response() res, @Body() login: LoginModel) {
        const user = await this.usersService.getUserByUsernameAsync(login.username);

        if (!user) {
            res.status(HttpStatus.NOT_FOUND).json({
                message: 'User Not Found',
            });
        } else {
            const token = this.authenticationService.createToken(user);
            return res.status(HttpStatus.OK).json(token);
        }
    }
}

在 Postman 中,我能够使用我的登录端点使用正确的凭据成功登录并接收 JWT 令牌。然后,我将 Authentication header 添加到 GET 请求,复制并粘贴到 JWT 令牌中,我已经尝试了“Bearer”和“JWT”方案以及 return 401 Unauthorized 如下图所示。

我使用了 JWT.IO 调试器来检查我的令牌是否有任何问题并且看起来是正确的:

我不知道这里可能是什么问题。任何帮助将不胜感激。

请注意,您的 JWT 策略中的 validate() 函数仅在 成功验证 JWT 后才被调用。如果您在尝试使用 JWT 时始终收到 401 响应,那么您不能指望调用此函数。

validate() 方法中的 return 被注入到任何受 JWT 身份验证保护的操作的请求 object 中。

我不确定您正在调用的 done() 函数,但这是我当前项目中的一个有效 validate() 方法:

async validate(payload: JwtPayload): Promise<User> {
  const { email } = payload
  const user = await this.authService.getActiveUser(email)

  if (!user) {
    throw new UnauthorizedException()
  }

  return user
}

看来您对 return 用户的渴望是正确的。确保这就是 authenticationService.validateUserToken() 实际所做的。

在策略中,jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() 似乎是正确的,并且在使用授权 header 和 Bearer TOKEN 的 Postman 中看起来也是正确的。

关于您的 authentication.controller.ts 文件,在 NestJS 的控制器中直接使用 @Request@Response object 时要小心。这些访问底层框架,例如Express 并有可能绕过 Nest 实现的许多功能。请参阅 https://docs.nestjs.com/faq/request-lifecycle 以查看您跳过的内容...

您可以 return object 直接从 NestJS 中的装饰控制器方法(例如 @Get()Post() 等)抛出错误,框架将采用剩下的关心:HTTP 代码,JSON,等等

从您的控制器考虑放弃 @Reponse res 并改用 throw new UnauthorizedException('User Not Found') 和简单的 return { token }(或类似)方法。

在您的受保护路由中,我发现显式声明 AuthGuard('jwt') 效果更好,并且在某些情况下不会产生警告,即使您确实将默认策略设置为 JWT。

您真的需要在登录路径上使用 AuthGuard('local') 吗?

在您的 loginAsync() 方法中,不要忘记使用有效负载实际签署令牌的关键步骤。您没有在身份验证服务中提供 createToken() 方法实现的代码,但我怀疑这可能是您遗漏的代码。

考虑登录服务的这个工作实现(由它的控制器的登录函数简单地调用):

  async login(authCredentialsDto: AuthCredentialsDto): Promise<{ accessToken: string }> {
    const { email, password } = authCredentialsDto

    const success = await this.usersRepository.verifyCredentials(email, password)

    if (!success) {
      throw new UnauthorizedException('Invalid credentials')
    }

    // roles, email, etc can be added to the payload - but don't add sensitive info!
    const payload: JwtPayload = { email } 
    const accessToken = this.jwtService.sign(payload)

    this.logger.debug(`Generated JWT token with payload ${JSON.stringify(payload)}`)

    return { accessToken }
  }

请注意,通过将 private jwtService: JwtService 添加到构造函数参数,通过依赖注入将 jwtService 注入 class。

还要注意上面是如何为 JwtPayload 定义接口的,所以它是显式类型的。这比在代码中使用 any 更好。

最后,如果您的 JWT 仍未通过验证,请绝对确定您在 Postman 中正确使用了您的令牌。 非常 小心不要添加 leading/trailing 空格、换行符等。我自己也犯过这个错误。您可能希望通过编写快速 JS 文件来尝试您的 API 并发出将授权 header 设置为值 Bearer ${token}.

的获取请求来进行健全性检查

希望对您有所帮助,祝您好运!

我有完全相同的 problem.My 问题是 JwtModule secret 和 JwtStrategy secretOrKey 不同。希望这可以帮助遇到此问题的人!

我有类似的 401 状态。我的问题是令牌过期时间真的很短(60 秒)。在测试 jwt 时也要确保有一个合理的有效期。

我有同样的问题

在我的案例中,验证端点参数 emailpassword 的问题 而 nest auth 文档指出它们应该是 usernamepassword,如下所示

async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }

还要注意在请求正文中发送用户名和密码

学分: https://github.com/nestjs/docs.nestjs.com/issues/875#issuecomment-619472086

我的是,我使用 RS256 算法对 JWT 进行签名,但出现“无效算法”错误。

所以我将“RS256”添加到我的“jwtStrategy”构造函数中,现在它看起来像这样:

constructor(private configService: ConfigService) {
super({
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  ignoreExpiration: false,
  algorithms:["RS256"],
  secretOrKey: configService.get('jwtPublicKey'),
});

}

然后它给了我一个错误,抱怨我的 public 密钥文件上的“没有起始行”,错误是我有一个 ssh-rsa 密钥格式而不是 rsa-pem 格式,我解决了这个方式:

Get PEM file from ssh-rsa key pair

终于成功了。

我得到了所有这些信息,在策略输出和守卫输出之间放置了一个记录器,这样做:

JWT Auth Guard example