【问题标题】:Getting User Data by using Guards (Roles, JWT)使用 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

感谢您的帮助。

【问题讨论】:

    标签: javascript node.js typescript nestjs


    【解决方案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;
        }
      
      }
      

      为什么要这样做?

      如果您查看AuthGuardsource 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)
            }
        

        【讨论】:

          猜你喜欢
          • 2017-08-12
          • 2017-01-11
          • 2019-03-15
          • 2020-11-01
          • 1970-01-01
          • 2019-07-01
          • 2021-10-14
          • 2020-07-15
          • 2016-07-27
          相关资源
          最近更新 更多