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

时间:2020-07-08 16:51:29

标签: passport.js nestjs jwt-auth passport-jwt

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

但是,即使在邮递员中展示了JWT令牌,它也会不断返回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令牌。然后,我向GET请求中添加一个Authentication头,复制并粘贴到JWT令牌中,并且我尝试了“ Bearer”和“ JWT”方案,并且都返回了401 Unauthorized下面的图片。

enter image description here

enter image description here

我使用了JWT.IO调试器来检查我的令牌是否存在问题,并且它看起来是正确的: enter image description here

我对这里可能出现的问题不知所措。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

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

来自return方法的validate()被注入到任何受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标头设置为值Bearer ${token}

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

答案 1 :(得分:1)

我遇到了完全相同的问题,我的问题是JwtModule secret和JwtStrategy secretOrKey不同。希望这可以帮助某人坚持下去!

答案 2 :(得分:0)

我的状态类似401。我的问题是令牌的有效期很短(60s)。测试jwt时,还要确保有一个合理的有效期。