使用Nest.js抽取一个CMS系统模板

本文介绍了使用Nest.js、React、CRA构建的CMS系统模板,涉及前后端技术栈、配置文件管理(如MySQL、Redis、JWT)、权限控制(基于RBAC的Role-BasedAccessControl)以及全局拦截器和路由守卫的实现。
摘要由CSDN通过智能技术生成

技术栈:前端是 antd + react + cra,后端是 nest + typeorm,数据库是 mysql + redis,API 文档用 swagger 生成,部署用 docker compose + pm2,网关使用 nginx。

项目连接: GitHub - zhaojiudong/cms_tem: CMS系统模板

全局注册:

拦截器(useGlobalInterceptors),通道(useGlobalPipes),过滤器(useGlobalFilters)都是在 main.ts里注册使用。

路由(Guard)是在app.ts 里注册使用 ,也可以在main里使用api全局使用

 providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: LoginGuard
    },
    {
      provide: APP_GUARD,
      useClass: PermissionGuard
    }
  ]

一、 CMS系统模板 必带功能   

 注册登录功能 

 角色鉴权功能

二、 要实现的特性

confjg配置文件

// app.moudle.ts

import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
 imports: [ 

    // 这里给项目注册了 ConfigMoudle 实例后, 就可以通过 ConfigService 去读取.env的变量
    ConfigModule.forRoot({
      isGlobal: true,
      // envFilePath: 'src/.env'
      envFilePath: path.join(__dirname, '.env')
    }),    

    //使用 ConfigService 去读取.env的变量例子   
    TypeOrmModule.forRootAsync({
      useFactory(configService: ConfigService) {
        return {
          type: "mysql",
          host: configService.get('mysql_server_host'),
          port: configService.get('mysql_server_port'),
          username: configService.get('mysql_server_username'),
          password: configService.get('mysql_server_password'),
          database: configService.get('mysql_server_database'),
          synchronize: true,
          logging: true,
          entities: [
            User, Role, Permission, MeetingRoom, Booking
          ],
          poolSize: 10,
          connectorPackage: 'mysql2',
          extra: {
              authPlugin: 'sha256_password',
          }
        }
      },
      inject: [ConfigService]   // 重点在这里,这个ConfigService就是已经被初始化过的对象
    }),

 ] 
 

 controllers: [AppController],
 

 providers: [
      AppService,
     {
      provide: APP_GUARD,
      useClass: LoginGuard
     },
     {
      provide: APP_GUARD,
      useClass: PermissionGuard
     }
    ]




// .env 

# redis 相关配置
redis_server_host=redis-container
redis_server_port=6379
redis_server_db=1

# nodemailer 相关配置
nodemailer_host=smtp.qq.com
nodemailer_port=587
nodemailer_auth_user=102xxxx375@qq.com
nodemailer_auth_pass=nkmdmgzkhjkkbfab

# mysql 相关配置
mysql_server_host=mysql-container
mysql_server_port=3306
mysql_server_username=root
mysql_server_password=guang
mysql_server_database=meeting_room_booking_system

# nest 服务配置
nest_server_port=3005

# jwt 配置
jwt_secret=guang
jwt_access_token_expires_time=30m
jwt_refresh_token_expres_time=7d

基于 RBAC  实现权限控制   ( Role Based Access Control,基于角色的权限控制 )

JWT 登录,权限问题   (tip:两个功能一起由下面代码实现)

// app.ts    

import { JwtModule } from '@nestjs/jwt';

// 引入 JWT模块实例  
// 使用方法 this.jwtService.sign { params: {} , config: {expiresIn : 30ms} }
// 解密: this.jwtService.verify(token)

@Module({
  imports: [
        JwtModule.registerAsync({
           global: true,
           useFactory(configService: ConfigService) {
              return {
                secret: configService.get('jwt_secret'),
                signOptions: {
                    expiresIn: '30m' // 默认 30 分钟
                }
              }
           },
          inject: [ConfigService]
       }),
    providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: LoginGuard
    },
    {
      provide: APP_GUARD,
      useClass: PermissionGuard
    }
  ]
 ]

使用LoginGuard、PermissionGuard 来做登录和权限的鉴权,根据 handler 上的 metadata 来确定要不要做鉴权、需要什么权限。

// LoginGuard.ts   
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';

 const requireLogin = this.reflector.getAllAndOverride('require-login', [
      context.getClass(),
      context.getHandler()
    ]);


    if(!requireLogin) {
      return true;
    }
    
    const authorization = request.headers.authorization;

    if(!authorization) {
      throw new UnauthorizedException('用户未登录');
    }

    try{
      const token = authorization.split(' ')[1];
      const data = this.jwtService.verify<JwtUserData>(token);

      request.user = {
        userId: data.userId,
        username: data.username,
        roles: data.roles,
        permissions: data.permissions
      }
      return true;
    } catch(e) {
      throw new UnauthorizedException('token 失效,请重新登录');
    }


// PermissionGuard.ts
import { Reflector } from '@nestjs/core';
import { Request } from 'express';

 const requiredPermissions = this.reflector.getAllAndOverride<string[]>('require-permission', [
      context.getClass(),
      context.getHandler()
    ])
    
    if(!requiredPermissions) {
      return true;
    }
    
    for(let i = 0; i < requiredPermissions.length; i++) {
      const curPermission = requiredPermissions[i];
      const found = permissions.find(item => item.code === curPermission);
      if(!found) {
        throw new UnauthorizedException('您没有访问该接口的权限');
      }
    }

    return true;

// controller层的文件  通过SetMetadata给这个路由增加权限

@Get('aaa')
@SetMetadata('require-login', true)
@SetMetadata('require-permission', ['ddd'])
aaaa() {
    return 'aaa';
}

输入守卫  , 输入输出拦截器, 封装context对象,把cookie提取到ctx对象上,输出时,把data封装到一个统一的输出对象上。

加一个修改响应内容的拦截器。把响应的格式改成 {code、message、data}

输入:

由上面实现 , 定义全局的路由守卫,然后给路由增加权限标识

@SetMetadata('require-login', true)    

路由就可以跟  全局路由守卫 产生联系

 输出:

// main.ts
 
 const app = await NestFactory.create<NestExpressApplication>(AppModule);

 app.useGlobalInterceptors(new FormatResponseInterceptor()); 


// format-response.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Response } from 'express';
import { map, Observable } from 'rxjs';

@Injectable()
export class FormatResponseInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const response = context.switchToHttp().getResponse<Response>();

    return next.handle().pipe(map((data) => {
      return {
        code: response.statusCode,
        message: 'success',
        data
      }
    }));
  }
}

三、要创建的表,要实现的modal层

实现 RBAC ,登陆功能需要三个Entity(实体) , 5个表 : 

用户表 User

角色表 Role

权限表 Premissions

用户-角色表 user_roles

角色-权限表 role_permissions

-----------------------------------

RBAC   ( Role Based Access Control,基于角色的权限控制 ) : 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值