1

我通过多个数据库实现多租户并使用jwt令牌作为授权,我担心的是当租户2的用户1登录并获取jwt令牌时,当他使用to令牌访问另一个租户时,他是否识别为租户的用户1 2?如果是这样,我们该如何解决?

我的策略

jwt.strategy.ts


@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly configService: ConfigService,

    private readonly moduleRef: ModuleRef,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      passReqToCallback: true,
      secretOrKey: configService.get('JWT_SECRET_KEY'),
    });
  }

  async validate(request: Request, jwtPayload: JwtPayload) {
    const contextId = ContextIdFactory.getByRequest(request);

    const authService: AuthService = await this.moduleRef.resolve(
      AuthService,
      contextId,
    );

    let { iat, exp } = jwtPayload;
    const timeDiff = exp - iat;

    if (timeDiff <= 0) {
      throw new UnauthorizedException();
    }
    return jwtPayload;
  }
}

我的认证服务

auth.service.ts


@Injectable({ scope: Scope.REQUEST })
export class AuthService {
  constructor(
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly userService: UsersService,
    private readonly auctionHouseService: AuctionHouseService,
  ) {}

  async createToken(user: User) {
    let plainUser: any = Object.assign({}, user);
    plainUser.auctionHouseId = (
      await this.auctionHouseService.getCurrentAuctionHouse()
    ).id;
    return {
      expiresIn: this.configService.get('JWT_EXPIRATION_TIME'),
      accessToken: this.jwtService.sign(plainUser),
    };
  }

}

我的登录控制器

auth.controller.ts


@Controller('api/auth')
@ApiUseTags('authentication')
export class AuthController {
  constructor(
    private readonly authService: AuthService,
    private readonly userService: UsersService,
  ) {}

  @Post('login')
  @ApiResponse({ status: 201, description: 'Successful Login' })
  @ApiResponse({ status: 400, description: 'Bad Request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async login(@Body() payload: LoginPayload, @Req() req): Promise<any> {
    let user = await this.authService.validateUser(payload);

    return omitPassword(await this.authService.createToken(user));
  }

4

1 回答 1

2

通常,识别 JWT 有效的域的正确方法是aud(或受众)字段。根据RFC7519

“aud”(受众)声明标识了 JWT 的目标接收者。打算处理 JWT 的每个主体都必须使用受众声明中的值来标识自己。如果当该声明存在时,处理该声明的主体未使用“aud”声明中的值来标识自己,则必须拒绝 JWT。在一般情况下,“aud”值是区分大小写的字符串数组,每个字符串都包含一个 StringOrURI 值。在 JWT 有一个受众的特殊情况下,“aud”值可能是包含 StringOrURI 值的单个区分大小写的字符串。受众价值的解释通常是特定于应用程序的。使用此声明是可选的。

在您的上下文中,您希望为每个租户选择唯一的受众(假设它是tenant1and tenant2),并且在生成 JWT 时,您会执行以下操作:

plainUser.aud = this.configService.get("JWT_TENANT")

在验证令牌时,您将引入一项检查,以确认aud声明与当前租户匹配。

let { iat, exp, aud } = jwtPayload;
if (aud !== this.configService.get("JWT_TENANT")) {
  throw new UnauthorizedException();
}

// The rest of your validation code

注意我假设您将为每个租户运行具有不同配置的应用程序的多个应用程序实例,但是如果租户是从其他地方派生的,那么您应该从相关数据源收集该信息。

以这种方式接近它将确保您可以识别用户所属的用户和租户,并防止tenant1使用令牌进行访问tenant2

于 2020-03-19T11:42:42.477 回答