5

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

但是,401 Unauthorized即使在 Postman 中提供 JWT 令牌,它也会不断返回。

这是我的代码:

用户控制器.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'),但这不起作用。

身份验证.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 {}

身份验证.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 令牌。然后,我在AuthenticationGET 请求中添加一个标头,复制并粘贴到 JWT 令牌中,我尝试了“Bearer”和“JWT”两种方案,并且都返回401 Unauthorized,如下图所示。

在此处输入图像描述

在此处输入图像描述

我使用了 JWT.IO 调试器来检查我的令牌是否有问题并且它看起来是正确的: 在此处输入图像描述

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

4

5 回答 5

11

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

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

我不确定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
}

看起来您在返回用户的愿望上走在了正确的轨道上。确保这就是authenticationService.validateUserToken()实际所做的。

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

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

您可以直接从 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 }
  }

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

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

最后,如果您的 JWT 仍未验证,请绝对确保您在 Postman 中正确使用您的令牌。要非常小心,不要添加前导/尾随空格、换行符等。我自己犯了这个错误。您可能希望通过编写一个快速的 JS 文件来尝试您的 API 并发出一个将 Authorization 标头设置为 value 的 fetch 请求来进行完整性检查Bearer ${token}

我希望这会有所帮助,祝你好运!

于 2020-07-08T23:51:31.767 回答
3

我遇到了完全相同的问题。我的问题是 JwtModule secret 和 JwtStrategy secretOrKey 不同。希望这可以帮助那些坚持这一点的人!

于 2020-10-03T17:26:41.817 回答
2

我的是我使用 RS256 算法来签署 JWT 并且我有一个“无效算法”错误。

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

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

}

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

从 ssh-rsa 密钥对获取 PEM 文件

最后它起作用了。

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

JWT Auth Guard 示例

于 2021-11-29T16:54:13.917 回答
1

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

于 2020-11-25T08:53:41.997 回答
0

我遇到过同样的问题

在我的情况下,验证端点参数的问题 是嵌套身份验证文档说明它们应该在哪里email以及在哪里, 如下所示passwordusernamepassword

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

于 2021-11-24T06:34:16.393 回答