JwtModule.registerAsync 在 NestJS 中不起作用

Posted

技术标签:

【中文标题】JwtModule.registerAsync 在 NestJS 中不起作用【英文标题】:JwtModule.registerAsync not working in NestJS 【发布时间】:2019-08-04 12:49:57 【问题描述】:

我正在开发一个 NestJS 项目,我需要将 JWT 与 .env 配置一起使用。它生成令牌,但在尝试访问安全 url(带有授权标头)时,它只返回未经授权的消息。

jwt.strategy.ts

import  Injectable, UnauthorizedException, Logger  from '@nestjs/common';
import  PassportStrategy  from '@nestjs/passport';
import  ExtractJwt, Strategy  from 'passport-jwt';
import  AuthService  from './auth.service';
import  JwtPayload  from './interfaces/jwt-payload.interface';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 

    constructor(private readonly authService: AuthService) 
        super(
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: process.env.JWT_SECRET_KEY,
        );
    

    async validate(payload: JwtPayload) 
        const user = await this.authService.validateUser(payload);
        if (!user) 
            throw new UnauthorizedException();
        

        return user;
    

auth.module.ts

import  Module  from '@nestjs/common';
import  AuthService  from './auth.service';
import  AuthController  from './auth.controller';
import  PassportModule  from '@nestjs/passport';
import  JwtModule  from '@nestjs/jwt';
import  JwtStrategy  from './jwt.strategy';

@Module(
  imports: [
    PassportModule.register( defaultStrategy: 'jwt' ),
    JwtModule.registerAsync(
      useFactory: async () => (
        secretOrPrivateKey: process.env.JWT_SECRET_KEY,
        signOptions: 
          expiresIn: process.env.JWT_EXPIRATION_TIME,
        ,
      ),
    ),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
)
export class AuthModule 

main.ts

import  NestFactory  from '@nestjs/core';
import * as dotenv from 'dotenv';
import  ApiModule  from './api/api.module';
import  Logger  from '@nestjs/common';

async function bootstrap() 
  dotenv.config( path: './.env');
  const app = await NestFactory.create(ApiModule);
  const port = process.env.APP_PORT;

  await app.listen(port);
  Logger.log(`Server started on http://localhost:$port`);

bootstrap();

看起来 JwtModule.registerAsync 没有使用环境变量。我尝试了很多东西,但总是失败。 如果我在auth.module.ts 中为静态数据更改环境变量,那么它工作正常。 像这样的:

secretOrPrivateKey: 'secretKey',
signOptions: 
  expiresIn: 3600,
,

更新 项目结构

- src
    - api
        - auth
            - interfaces
                jwt-payload.interface.ts
            auth.controller.ts
            auth.module.ts
            auth.service.ts
            jwt.strategy.ts
            index.ts
        api.module.ts
        index.ts
    main.ts
- test
.env

我的 main.ts 现在看起来像这样。

import  NestFactory  from '@nestjs/core';
import * as dotenv from 'dotenv';
import  resolve  from 'path';
import  ApiModule  from './api/api.module';
import  Logger  from '@nestjs/common';

async function bootstrap() 
  dotenv.config( path: resolve(__dirname, '../.env') );
  const app = await NestFactory.create(ApiModule);
  const port = process.env.APP_PORT;

  await app.listen(port);
  Logger.log(`Server started on http://localhost:$port`);

bootstrap();

您看到我的.env 在项目的根目录中。

【问题讨论】:

【参考方案1】:

对我来说,你的代码有效:

您的.env 文件在哪里? 您的配置dotenv.config( path: './.env'); 等于默认配置dotenv.config();,其中.env 文件在您的项目根目录(而不是src)中查找。

如果你想把你的.env文件放到src目录下,使用下面的配置

import  resolve  from 'path';
dotenv.config( path: resolve(__dirname, '.env') );

我建议不要直接使用您的环境变量,而是将它们封装在ConfigService 中,请参阅docs。这使得测试和重构变得更加容易。

【讨论】:

这很奇怪,我不想在我的本地主机上工作。也许节点版本或什么?我有节点版本 v11.5.0 你把你的.env文件放在哪里了? 我会用我的项目结构更新我的帖子。我想知道 nodemon 是否与此有关 别这么认为,codesandbox的例子也运行nodemon 我不知道它可能是什么:S。当然,我在本地也有同样的东西,但它每次都会引发未经授权的事情。【参考方案2】:

如果你使用config module,你可以这样做:

JwtModule.registerAsync(
  useFactory: (config: ConfigService) => 
    return 
      secret: config.get<string>('JWT_SECRET_KEY'),
      signOptions: 
        expiresIn: config.get<string | number>('JWT_EXPIRATION_TIME'),
      ,
    ;
  ,
  inject: [ConfigService],
),

我在初始化JwtModule 时也遇到了问题,这段代码解决了它。

【讨论】:

我在从 env 文件中读取密钥时遇到问题,这个解决方案对我有用。【参考方案3】:

TL;DR

假设您有一个 .env 文件并且它位于正确的位置,为此您需要在一切之前配置 dotenv,甚至导入

// configure dotenv before every thing, even imports
import * as dotenv from 'dotenv';
import  resolve  from 'path';
dotenv.config( path: resolve(__dirname, '../.env') );

// rest of the code
import  NestFactory  from '@nestjs/core';
import  ApiModule  from './api/api.module';
import  Logger  from '@nestjs/common';

async function bootstrap() 
  const app = await NestFactory.create(ApiModule);
  const port = process.env.APP_PORT;

  await app.listen(port);
  Logger.log(`Server started on http://localhost:$port`);

bootstrap();

为什么?

因为当你做这样的事情时

import  ApiModule  from './api/api.module'

发生的情况是您正在运行文件 ./api/api.module 中的代码,该文件将如下所示(我正在使用您在问题中显示的另一个文件,以便您更清楚)

import  Module  from '@nestjs/common';
import  AuthService  from './auth.service';
import  AuthController  from './auth.controller';
import  PassportModule  from '@nestjs/passport';
import  JwtModule  from '@nestjs/jwt';
import  JwtStrategy  from './jwt.strategy';

@Module(
  imports: [
    PassportModule.register( defaultStrategy: 'jwt' ),
    JwtModule.registerAsync(
      useFactory: async () => (
        secretOrPrivateKey: process.env.JWT_SECRET_KEY,
        signOptions: 
          expiresIn: process.env.JWT_EXPIRATION_TIME,
        ,
      ),
    ),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
)
export class AuthModule 

当你导入它时,整个文件被“执行”,process.env 的引用在你设置dotenv 之前被“设置”。

因此,在“执行”使用process.env 的代码之前,您需要“运行”设置dotenv 的代码。

观察:

我仍然建议使用已经内置的Configuration,然后您应该使用异步方法并注入配置服务(就像其他一些答案一样)。

但如果你确实想使用process.env,那么在一切之前设置dotenv 是可行的方法。

【讨论】:

以上是关于JwtModule.registerAsync 在 NestJS 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据