nestjs JWT认证

注:该文档目前只对jwt做处理,缺少登录其它校验、以及redis部分,后续更新

1、安装

npm install --save @nestjs/jwt passport-jwt
npm install --save @nestjs/passport passport
npm install --save-dev @types/passport-jwt

2、注册jwt 文档

全局模块中引入jwtModule,提供密钥及过期时间,并导出,为后面注入做准备

import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import configurationService from '../config/index';
import { JwtModule } from '@nestjs/jwt';

@Global()
@Module({
    imports: [
        ConfigModule.forRoot({
            cache: true,
            load: [configurationService],
            isGlobal: true,
        }),
        TypeOrmModule.forRootAsync({
            inject: [ConfigService],
            useFactory: (config: ConfigService) => {
                return ({
                    type: "mysql",
                    autoLoadEntities: true,     //自动加载实体
                    timezone: '+08:00',
                    ...config.get("db.mysql"),
                } as TypeOrmModuleOptions)
            }
        }),
        JwtModule.registerAsync({
            inject: [ConfigService],
            useFactory: (config: ConfigService) => {
                return {
                    secret: config.get("jwt.secretkey"),
                    signOptions: {
                        expiresIn: config.get("jwt.expiresin"),
                    },
                }
            }
        })
    ],
    providers: [
       
    ],
    exports: [
        TypeOrmModule,
        JwtModule
    ],
})
export class ShareModule { }

3、登录成功后使用JwtServie生成密钥 文档

调用jwtService.signAsync(信息)生成Token

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class UsersService {
  constructor(
    private jwtService: JwtService,
  ) { }
  async login() {
    const payload = {userId:"通过登录成功后拿到的用户id或者其他信息"}
    return await this.jwtService.signAsync(payload);
  }
}

4、定义jwt解析,如果payload.userId不为空,抛出异常,如果为真,可以通过@Req中的user进行获取 文档

import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtAuthStrategy extends PassportStrategy(Strategy) {
    constructor(private readonly config: ConfigService) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: config.get('jwt.secretkey')!,
        });
    }
    validate(payload: any) {
        if(!payload.userId){
            throw new UnauthorizedException('当前登录已过期,请重新登录')
        }else{
            return payload;
        }
    }
}

5、路由守卫,用来做拦截 文档

如果调用的super.canActivate(context),则会走JwtAuthStrategy类的校验,反之则不会走

import { ExecutionContext, Inject, Injectable, UnauthorizedException } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";
import { JwtAuthStrategy } from './jwt-auth.strategy';
import { ConfigService } from '@nestjs/config';
import { pathToRegexp } from 'path-to-regexp';
import { isIn } from "class-validator";
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
    private globalWhiteList: { path: string, method: string }[] = [];

    constructor(
        private readonly reflector: Reflector,
        private readonly configService: ConfigService,
        private readonly jwtAuthStrategy: JwtAuthStrategy
    ) {
        super();
        this.globalWhiteList = this.configService.get("perm.router.whitelist") || [];
    }
    async canActivate(context: ExecutionContext): Promise<boolean> {
        const isPublic = this.reflector.getAllAndOverride('isPublic', [
            context.getHandler(),
            context.getClass()
        ])
        if (isPublic) {
            return true
        }

        const isINWhiteList = this.checkWhiteList(context);
        if(isINWhiteList){
            return true
        }

        const req = context.switchToHttp().getRequest();
        if (!req.headers.authorization) {
            throw new UnauthorizedException('当前登录已过期,请重新登录')
        }
        return super.canActivate(context) as boolean
    }

    checkWhiteList(ctx: ExecutionContext): boolean {
        const req = ctx.switchToHttp().getRequest();
        const i = this.globalWhiteList.findIndex((route) => {
            // 请求方法类型相同
            if (!route.method || req.method.toUpperCase() === route.method.toUpperCase()) {
                // 对比 url
                const { regexp } = pathToRegexp(route.path);
                return !!regexp.exec(req.route.path);
            }
            return false;
        });
        // 在白名单内 则 进行下一步, i === -1 ,则不在白名单,需要 比对是否有当前接口权限
        return i > -1;
    }
}

 上述代码解读:因为接下来要做的是定义全局守卫,会对所有路由都进行一个过滤,那么我们路由中比如登录、注册、验证码等等这些是不需要进行验证的,就有了一个获取元数据中是否存在isPublic,如果存在,就直接返回true,不在进行拦截
isPublic添加方法:
        1、控制器添加@SetMetadata('isPublic', true)

  @Get()
  @SetMetadata('isPublic', true)
  findAll() {
    return this.usersService.findAll();
  }

      2、自定义装饰器,添加isPublic属性:

                

import { SetMetadata } from "@nestjs/common";
export const SkipAuth = () => SetMetadata("isPublic", true)
  @Get()
  @SkipAuth()
  findAll() {
    return this.usersService.findAll();
  }

上述代码解读:

checkWhiteList:白名单,可以自己定义不需要拦截的路由,以下是白名单配置格式

perm:
  router:
    whitelist:
      [
        { path: '/captchaImage', method: 'GET' },
        { path: '/registerUser', method: 'GET' },
        { path: '/register', method: 'POST' },
        { path: '/login', method: 'POST' },
        { path: '/logout', method: 'POST' },
        { path: '/perm/{id}', method: 'GET' },
        { path: '/upload', method: 'POST' },
      ]

注:白名单和isPublic方式用一种就好啦

6、注册全局守卫

将JwtAuthStrategy和JwtAuthGuard进行引入,注意顺序,因为JwtAuthGuard中存在JwtAuthStrategy的注入,所有JwtAuthStrategy必须在JwtAuthGuard之前

import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import configurationService from '../config/index';
import { join } from 'path';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { JwtAuthGuard } from '../utils/auth/jwt-auth.guard';
import { JwtAuthStrategy } from '../utils/auth/jwt-auth.strategy';
@Global()
@Module({
    imports: [
        ConfigModule.forRoot({
            cache: true,
            load: [configurationService],
            isGlobal: true,
        }),
        TypeOrmModule.forRootAsync({
            inject: [ConfigService],
            useFactory: (config: ConfigService) => {
                return ({
                    type: "mysql",
                    autoLoadEntities: true,     //自动加载实体
                    timezone: '+08:00',
                    ...config.get("db.mysql"),
                } as TypeOrmModuleOptions)
            }
        }),
        JwtModule.registerAsync({
            inject: [ConfigService],
            useFactory: (config: ConfigService) => {
                return {
                    secret: config.get("jwt.secretkey"),
                    signOptions: {
                        expiresIn: config.get("jwt.expiresin"),
                    },
                }
            }
        }),
    ],
    providers: [
        JwtAuthStrategy,
        {
            provide: APP_GUARD,
            useClass: JwtAuthGuard,
        }
    ],
    exports: [
        TypeOrmModule,
        JwtAuthStrategy,
        JwtModule
    ],
})
export class ShareModule { }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值