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

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

使用 Postman 测试我的端点,我能够成功“登录”并接收 JWT token 。现在,我试图点击一个据说有 AuthGuard 的端点。以确保现在我已登录,我现在可以访问它。
但是,它不断返回401 Unauthorized即使在 Postman 中提供了 JWT token 。
这是我的代码:
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 token 。然后,我添加一个 Authentication头到 GET 请求,复制并粘贴到 JWT token 中,我尝试了“承载”和“JWT”方案,都返回 401 Unauthorized如下图所示。
enter image description here
enter image description here
我使用了 JWT.IO 调试器来检查我的 token 是否有任何问题并且它看起来是正确的:
enter image description here
我不知道这里可能是什么问题。任何帮助将不胜感激。

最佳答案

请注意 validate()只有在成功验证 JWT 后才会调用 JWT 策略中的函数。如果您在尝试使用 JWT 时始终收到 401 响应,那么您不能期望调用此函数。return来自 validate()方法被注入(inject)到任何受 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 中使用带有 Bearer TOKEN 的 Authorization header 看起来也正确。
关于您的 authentication.controller.ts文件,小心使用 @Request@Response直接在 NestJS Controller 中的对象。这些访问底层框架,例如Express 并且有可能绕过 Nest 实现的许多功能。引用 https://docs.nestjs.com/faq/request-lifecycle看看你在跳过什么...
您可以直接从 NestJS 中的装饰 Controller 方法(例如 @Get()Post() 等)返回对象并抛出错误,框架将处理其余部分:HTTP 代码、JSON 等。
从您的 Controller 考虑放弃 @Reponse res并使用 throw new UnauthorizedException('User Not Found')和一个简单的 return { token } (或类似)方法代替。
在您 protected route ,我发现明确声明 AuthGuard('jwt')在某些情况下效果更好并且不会产生警告,即使您确实将默认策略设置为 JWT。
你真的需要AuthGuard('local')吗?在您的登录路线上?
在您的 loginAsync() 内方法不要忘记使用有效负载实际签署 token 的关键步骤。您没有提供 createToken() 的代码方法实现在您的身份验证服务中,但我怀疑这可能是您所缺少的。
考虑登录服务的这个工作实现(它被 Controller 的登录函数简单地调用):
  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 通过依赖注入(inject)注入(inject)到类中到构造函数参数。
还要注意上面是如何为 JwtPayload 定义接口(interface)的。所以它是显式输入的。这比使用 any 更好就像您在代码中一样。
最后,如果您的 JWT 仍未通过验证,请务必确保您在 Postman 中正确使用了您的 token 。非常小心,不要添加前导/尾随空格、换行符等。我自己也犯过这个错误。您可能希望通过编写一个快速的 JS 文件来尝试您的 API 并发出一个获取请求来进行完整性检查,该请求将 Authorization header 设置为值 Bearer ${token} .
我希望这会有所帮助,祝你好运!

关于passport.js - Nest.js Auth Guard JWT 身份验证不断返回 401 未经授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62799708/

相关文章:

node.js - Strategy.OAuth2Strategy.parseErrorResponse 错误 - NodeJS passport google oauth2.0

javascript - 为什么 res.redirect 实际上没有重定向我?

javascript - 使用@UploadFile上传文件期间接收到的对象类型

firebase - 您从 Firebase 哪里获取/找到 JWT-Secret?

node.js - 未处理的拒绝 TypeError : dbUser. validPassword 不是函数 Sequelize Node React Passport

javascript - NestJS 哈希密码

postgresql - TypeORM postgresql 急切加载关系定义为惰性

jwt - 为什么在 jwt header 中发送算法

session - 有环回 + Passport + jwt 的例子吗?