使用 Guards (Roles, JWT) 获取用户数据
Posted
技术标签:
【中文标题】使用 Guards (Roles, JWT) 获取用户数据【英文标题】:Getting User Data by using Guards (Roles, JWT) 【发布时间】:2019-04-24 20:22:40 【问题描述】:这里的文档有点薄,所以我遇到了问题。我尝试使用 Guards 来保护 Controller 或它的 Actions,所以我会询问经过身份验证的请求的角色(通过 JWT)。在我的 auth.guard.ts 中,我要求输入“request.user”,但它是空的,所以我无法检查用户角色。我不知道如何定义“request.user”。这是我的身份验证模块,它是导入的。
auth.controller.ts
import Controller, Get, UseGuards from '@nestjs/common';
import AuthGuard from '@nestjs/passport';
import AuthService from './auth.service';
import RolesGuard from './auth.guard';
@Controller('auth')
export class AuthController
constructor(private readonly authService: AuthService)
@Get('token')
async createToken(): Promise<any>
return await this.authService.signIn();
@Get('data')
@UseGuards(RolesGuard)
findAll()
return message: 'authed!' ;
roles.guard.ts
这里 user.request 是空的,因为我从来没有定义它。文档没有显示如何或在哪里。
import Injectable, CanActivate, ExecutionContext from '@nestjs/common';
import Reflector from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate
constructor(private readonly reflector: Reflector)
canActivate(context: ExecutionContext): boolean
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles)
return true;
const request = context.switchToHttp().getRequest();
const user = request.user; // it's undefined
const hasRole = () =>
user.roles.some(role => !!roles.find(item => item === role));
return user && user.roles && hasRole();
auth.module.ts
import Module from '@nestjs/common';
import AuthService from './auth.service';
import HttpStrategy from './http.strategy';
import UserModule from './../user/user.module';
import AuthController from './auth.controller';
import JwtStrategy from './jwt.strategy';
import PassportModule from '@nestjs/passport';
import JwtModule from '@nestjs/jwt';
@Module(
imports: [
PassportModule.register( defaultStrategy: 'jwt' ),
JwtModule.register(
secretOrPrivateKey: 'secretKey',
signOptions:
expiresIn: 3600,
,
),
UserModule,
],
providers: [AuthService, HttpStrategy],
controllers: [AuthController],
)
export class AuthModule
auth.service.ts
import Injectable from '@nestjs/common';
import UserService from '../user/user.service';
import JwtService from '@nestjs/jwt';
@Injectable()
export class AuthService
constructor(
private readonly userService: UserService,
private readonly jwtService: JwtService,
)
async signIn(): Promise<object>
// In the real-world app you shouldn't expose this method publicly
// instead, return a token once you verify user credentials
const user: any = email: 'user@email.com' ;
const token: string = this.jwtService.sign(user);
return token ;
async validateUser(payload: any): Promise<any>
// Validate if token passed along with HTTP request
// is associated with any registered account in the database
return await this.userService.findOneByEmail(payload.email);
jwt.strategy.ts
import ExtractJwt, Strategy from 'passport-jwt';
import AuthService from './auth.service';
import PassportStrategy from '@nestjs/passport';
import Injectable, UnauthorizedException from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy)
constructor(private readonly authService: AuthService)
super(
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'secretKey',
);
async validate(payload: any)
const user = await this.authService.validateUser(payload);
if (!user)
throw new UnauthorizedException();
return user;
文档:https://docs.nestjs.com/guards
感谢您的帮助。
【问题讨论】:
【参考方案1】:如果你使用req.authInfo
,它是否有效?
只要您不提供对 passport.authenticate 方法的自定义回调,用户数据就应该像这样附加到请求对象。
req.authInfo
应该是您在 validate
方法中返回的对象
【讨论】:
【参考方案2】:除了RolesGuard
,您还需要使用AuthGuard
。
标准
您可以使用将用户对象附加到请求的标准AuthGuard
实现。当用户未经身份验证时,它会引发 401 错误。
@UseGuards(AuthGuard('jwt'))
扩展
如果您因为需要不同的行为而需要编写自己的守卫,请扩展原始AuthGuard
并覆盖您需要更改的方法(示例中为handleRequest
):
@Injectable()
export class MyAuthGuard extends AuthGuard('jwt')
handleRequest(err, user, info: Error)
// don't throw 401 error when unauthenticated
return user;
为什么要这样做?
如果您查看AuthGuard
的source code,您可以看到它将用户附加到请求作为对护照方法的回调。如果您不想使用/扩展AuthGuard
,则必须实现/复制相关部分。
const user = await passportFn(
type || this.options.defaultStrategy,
options,
// This is the callback passed to passport. handleRequest returns the user.
(err, info, user) => this.handleRequest(err, info, user)
);
// Then the user object is attached to the request
// under the default property 'user' which you can change by configuration.
request[options.property || defaultOptions.property] = user;
【讨论】:
它可以工作,因为我使用 @UseGuards(AuthGuard('jwt'), RolesGuard) 作为装饰器。我为我的 RolesGuard 扩展了 AuthGuard 并覆盖了这些函数,但似乎它们没有被调用。 我很高兴它现在可以工作了。 :-) 我认为从单一责任的角度来看,将它们分开是有意义的。如果您仍想扩展您的情况,您可能需要覆盖canActivate(...)
,然后从内部调用 super.canActivate(...)
。【参考方案3】:
您可以将多个警卫附加在一起(@UseGuards(AuthGuard('jwt'), RolesGuard))以在它们之间传递上下文。然后您将可以访问“RolesGuard”中的“req.user”对象。
【讨论】:
【参考方案4】:在我得到选择的答案后(谢谢),我也发现了这个选项,你可以将它添加到本质上做同样事情的构造函数中。
http://www.passportjs.org/docs/authorize/
验证回调中的关联
上述方法的一个缺点是它需要两个 相同策略和支持路线的实例。
为避免这种情况,请将策略的 passReqToCallback 选项设置为 true。 启用此选项后, req 将作为第一个参数传递给 验证回调。
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy, 'local')
constructor(private authService: AuthService)
super(
passReqToCallback: true
)
// rest of the strategy (validate)
【讨论】:
以上是关于使用 Guards (Roles, JWT) 获取用户数据的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有守卫装饰器的情况下始终验证 JWT? (Nest.js + 护照)
数据库自动化运维2-Restful API JWT and Redis authorization