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 时也要确保有一个合理的有效期。
我有同样的问题
在我的案例中,验证端点参数 email
和 password
的问题
而 nest auth 文档指出它们应该是 username
和 password
,如下所示
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
终于成功了。
我得到了所有这些信息,在策略输出和守卫输出之间放置了一个记录器,这样做:
使用 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 时也要确保有一个合理的有效期。
我有同样的问题
在我的案例中,验证端点参数 email
和 password
的问题
而 nest auth 文档指出它们应该是 username
和 password
,如下所示
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
终于成功了。
我得到了所有这些信息,在策略输出和守卫输出之间放置了一个记录器,这样做: