NestJs - jwt 详细配置步骤

426 阅读2分钟
  1. npm install

    $ yarn add passport passport-jwt passport-local @nestjs/passport @nestjs/jwt -S
    
  2. 创建 auth 文件

    nest g service auth logical
    nest g module auth logical
    
  3. 新建 auth 常量文件

    // src/logical/auth/constats.ts
    export const jwtConstants = {
      secret: 'shinobi7414' // 秘钥
    };
    
  4. 新建 JWT 策略

    // src/logical/auth/jwt.strategy.ts
    import { ExtractJwt, Strategy } from 'passport-jwt';
    import { PassportStrategy } from '@nestjs/passport';
    import { Injectable } from '@nestjs/common';
    import { jwtConstants } from './constants';
    
    @Injectable()
    export class JwtStrategy extends PassportStrategy(Strategy) {
      constructor() {
        super({
          jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
          ignoreExpiration: false,
          secretOrKey: jwtConstants.secret,
        });
      }
      
      // JWT验证 - Step 4: 被守卫调用
      async validate(payload: any) {
        console.log(`JWT验证 - Step 4: 被守卫调用`);
        return { userId: payload.sub, username: payload.username, realName: payload.realName, role: payload.role };
      }
    }
    
  5. 编写 auth.service.ts 逻辑

    // src/logical/auth/auth.service.ts
    import { Injectable } from '@nestjs/common';
    import { JwtService } from '@nestjs/jwt';
    import * as md5 from 'md5';
    import { Admin } from '../../admin/entities/admin.entity';
    
    const encryptPassword = (pw, salt) => md5(`${md5(pw)}${salt}`);
    
    @Injectable()
    export class AuthService {
      constructor(private readonly jwtService: JwtService) {}
    
      // JWT验证 - Step 2: 校验用户信息
      async validateUser(user: Admin, password: string): Promise<any> {
        console.log('JWT验证 - Step 2: 校验用户信息');
        if (user) {
          const hashedPassword = user.password;
          const salt = user.salt;
          // 通过密码盐,加密传参,再与数据库里的比较,判断是否相等
          const hashPassword = encryptPassword(password, salt);
          if (hashedPassword === hashPassword) {
            // 密码正确
            const res = this.certificate(user);
            console.log(res);
            return res;
          } else {
            // 密码错误
            return {
              code: 2,
              user: null,
            };
          }
        }
        // 查无此人
        return {
          code: 3,
          user: null,
        };
      }
    
      // JWT验证 - Step 3: 处理 jwt 签证
      async certificate(user: any) {
        const payload = {
          username: user.username,
          sub: user.userId,
          realName: user.realName,
          role: user.role,
        };
        console.log('JWT验证 - Step 3: 处理 jwt 签证');
        try {
          const token = this.jwtService.sign(payload);
          return {
            code: 200,
            data: {
              token,
            },
            msg: `登录成功`,
          };
        } catch (error) {
          return {
            code: 600,
            msg: `账号或密码错误`,
          };
        }
      }
    }
    
    
  6. 编辑 auth.module.ts

    // src/logical/auth/auth.module.ts
    import { Module } from '@nestjs/common';
    import { AuthService } from './auth.service';
    import { JwtStrategy } from './jwt.strategy';
    import { PassportModule } from '@nestjs/passport';
    import { JwtModule } from '@nestjs/jwt';
    import { jwtConstants } from './constants';
    import { Admin } from '../../admin/entities/admin.entity';
    import { TypeOrmModule } from '@nestjs/typeorm';
    
    @Module({
      imports: [
        TypeOrmModule.forFeature([Admin]),
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
          secret: jwtConstants.secret,
          signOptions: { expiresIn: '8h' }, // token 过期时效
        }),
      ],
      providers: [AuthService, JwtStrategy],
      exports: [AuthService],
    })
    export class AuthModule {}
    
    
  7. 编辑 app.module.ts

    import { AuthModule } from './logical/auth/auth.module';
    // 仅增加 AuthModule 的引入, 如果 AuthService 被引入,要删除
    @Module({
      imports: [UserModule, AuthModule],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}
    
  8. 编辑 user.module.ts

    import { AuthModule } from 'src/logical/auth/auth.module';
    
    // imports 仅增加 AuthModule
    @Module({
      imports: [AuthModule],
    })
    
    
  9. 编辑 user.service.ts(我这里的模块命名为 Admin)

    // 增加导入
    import { AuthService } from 'src/logical/auth/auth.service';
    
    @Injectable()
    export class AdminService {
      constructor(
        @InjectRepository(Admin) private admin: Repository<Admin>,
        // 增加声明
        private readonly authService: AuthService,
      ) {}
      
    
      async login({username, password}: {username: string; password: string}) {
        const user = await this.admin.findOne({
          where: {
            username,
          },
        });
        // 增加登录校验
        const data = await this.authService.validateUser(user, password)
        return data;
      }
    }
    
    
  10. 编辑 user.controller.ts

    import {
      Get,
      Query,
      Bind,
      UseGuards,
    } from '@nestjs/common';
    import { AdminService } from './admin.service';
    import { Admin } from './entities/admin.entity';
    import { AuthGuard } from '@nestjs/passport';
    
    @Controller('admin')
    export class AdminController {
      constructor(private readonly adminService: AdminService) {}
    
      // 增加路由守卫
      @UseGuards(AuthGuard('jwt'))
      @Get('/detail')
      @Bind(Query())
      findOne(query: { id?: number; mobile?: string; email?: string }) {
        return this.adminService.findOne(query);
      }
    }
    
    
  11. 创建自定义守卫 @Public,再也不用傻傻地每个接口地写 @UseGuards(AuthGuard('jwt'))

    1. 创建装饰器文件

      //  /src/common/decorator/public.decorator.ts
      import { SetMetadata } from '@nestjs/common';
      export const Public = () => SetMetadata('isPublic', true);
      
    2. 创建全局守卫

      1. 创建 guard 文件

        //   /src/common/guards/auth.guard.ts
        import { ExecutionContext, Injectable } from '@nestjs/common';
        import { Reflector } from '@nestjs/core';
        import { AuthGuard } from '@nestjs/passport';
        
        @Injectable()
        export class JwtAuthGuard extends AuthGuard('jwt') {
          constructor(private reflector: Reflector) {
            super();
          }
        
          canActivate(context: ExecutionContext) {
            const isPublic = this.reflector.getAllAndOverride('isPublic', [
              context.getHandler(),
              context.getClass(),
            ]);
        
            if (isPublic) return true;
        
            return super.canActivate(context);
          }
        }
        
        
      2. 编辑 app.module.ts

        //     /src/app.module.ts
        import { resolve } from 'path';
        import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
        import { ConfigModule } from 'nestjs-config';
        import { ScheduleModule } from '@nestjs/schedule';
        import { APP_GUARD } from '@nestjs/core';
        import { JwtAuthGuard } from './common/guards/auth.guard';
        @Module({
          imports: [
           ],
          providers: [
            {
              provide: APP_GUARD,
              useClass: JwtAuthGuard,
            },
          ],
        })
        export class AppModule {
          configure(consumer: MiddlewareConsumer) {}
        }
        
        
    3. 使用 @public

      import { Controller, Get, Post, Body, Param, Request } from '@nestjs/common';
      import { Public } from 'src/common/decorator/public.decorator';
      import { AuthorizationService } from './authorization.service';
      
      @Controller('canteenApp/authorization')
      export class AuthorizationController {
        constructor(private readonly authorizationService: AuthorizationService) {}
        // 在公开的接口里声明 @Public() 就不用走鉴权了
        @Public()
        @Post('/login')
        async login(@Body() body) {
          return await this.authorizationService.login(body.username, body.password);
        }
      }
      
      
  12. 已完成

参考

Nest.js 从零到壹系列(三):使用 JWT 实现注册、登录 juejin.cn/post/684490…

NestJS jwt实现token验证,并使用自定义Guard来使无需专门每个接口配置Guard鉴权 blog.csdn.net/baidu_39340…

nest.js学习笔记(五) --jwt验证 www.cnblogs.com/venblogs/p/…