如何使用 prisma 在 NestJs 中仅获取当前用户相关内容

问题描述 投票:0回答:3

我正在创建一个多公司应用程序,登录用户只能访问他的内容。目前我不想在这个应用程序中实现多租户。

我创建了一个装饰器来获取当前登录的用户:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

并在我的控制器操作上使用此装饰器来获取用户的 companyId 并将此 ID 传递给服务。

 @Get()
  async getAll(
    @CurrentUser()
    currentUser,
  ) {
    return this.categoryService.getAll(currentUser.companyId);
  }

现在,在服务中我可以访问companyId并可以访问他的内容:

  async getAll(companyId: string) {
    const categories = await this.prisma.category.findMany({
      where: { companyId: companyId },
    });

    return categories;
  }

最大的问题是我必须在每个控制器、服务等中重复这个过程。

有没有更简单的方法来做到这一点?我可以在服务级别上使用什么吗?我该如何解决这个问题?

nestjs prisma
3个回答
0
投票

您好,最简单的方法之一是

userGuard
,您需要
jwt token
才能使用它。现在你可能想知道什么是useguards以及它们是如何使用的。我想给你一个真实的例子,

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy,ExtractJwt } from "passport-jwt";


@Injectable()
export class  JwtStrategy extends PassportStrategy(Strategy,'jwt') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'super-secret-cat',
    });
  }
  async validate(payload: any) {
    
 
    return payload
  }
}

这是一个简单的 JWT 防护。首先检查 jwt 登录,然后使用

@UseGuards(AuthGuard("jwt")

您验证了用户的身份 了解更多信息 https://docs.nestjs.com/guards


0
投票

不确定你到底想要实现什么,但我会这样做:

/* File: CategoryService.ts */

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

@Injectable({ scope: Scope.REQUEST })
export class CategoryService {
  // declare your variables/properties

  constructor(@Inject(REQUEST) private request: Request) {

  }

  // get current user
  getCurrentUserCompanyId() {
    return this.request.user['companyId'];
  }

  async getAll() {
    const categories = await this.prisma.category.findMany({
      where: { companyId: this.getCurrentUserCompanyId() },
    });

    return categories;
  }
}


/* File: category.controller.ts */
@Controller('category')
export class CategoryController {

  constructor(private categoryService: CategoryService) {}

  @Get()
  async getAll() {
    return this.categoryService.getAll();
  }
}

0
投票

我在同一个地方。经过几个小时的研究,终于找到了这个用例的解决方案。 (尚未在生产环境中运行它看到结果。)

基本上,主要技巧是使用这个库来访问连接的用户数据。 一旦这一部分完成,为了确保没有人(包括开发人员)访问意外数据,我有 Prisma 中间件(它也具有软删除功能,这是题外话,但不想排除它,因为我假设我们都做吧。)

这是我的 PrismaClient 代码:

import { Injectable, OnModuleInit } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { __CLS_CONN_USER, __DEFAULT_COMPANY_CODE } from '../../utils/constants';
import { ClsService } from 'nestjs-cls';
import { UserEntity } from '../users/entities/user.entity';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  constructor(
    private readonly cls: ClsService
  ) {}

  async onModuleInit() {
    await this.$connect();
    this.$use(this.softDeleteAndCompanyCodeMiddleware);
    
    //For the future: use $extends => https://github.com/prisma/prisma/issues/18628
  }

  getConnUserCompanyCode(): string {
    let companyCode = __DEFAULT_COMPANY_CODE;
    const connUser = this.cls.get(__CLS_CONN_USER) as UserEntity;
    if (connUser) {
      companyCode = connUser.companyCode;
    }
    return companyCode;
  }

  softDeleteAndCompanyCodeMiddleware: Prisma.Middleware = async (params, next) => {

    if (params.action === 'findUnique' || params.action === 'findFirst' || params.action === 'count') {
      const obj = {
        ...params,
        action: params.action,
        args: {
          ...params.args,
          where: {
            ...params.args?.where,
            isDeleted: false,//soft-delete, bring only undeleted
          },
        },
      }
      //do not apply for User Login action.
      if (params.model !== 'User')
        obj.args.where.companyCode = this.getConnUserCompanyCode();//bring only records within the same company

      return next(obj);
    }

    if (params.action === 'findMany') {
      return next({
        ...params,
        args: {
          ...params.args,
          where: {
            ...params.args?.where,
            isDeleted: false,//soft-delete, bring only undeleted
            companyCode: this.getConnUserCompanyCode()//bring only records within the same company
          },
        },
      });
    }

    if (params.action === 'create') {
      return next({
        ...params,
        args: {
          data: {
            ...params.args.data,
            isDeleted: false,//never allow the developers to create a record as deleted.
            companyCode: this.getConnUserCompanyCode()//make sure no recorded is created for a different company
          }
        },
      });
    }

    if (params.action === 'update') {
      return next({
        ...params,
        args: {
          data: {
            ...params.args.data,
            companyCode: undefined//never let users to update companyCode field
          },
          where: {
            ...params.args?.where,
            isDeleted: false,
            companyCode: this.getConnUserCompanyCode()//make sure to only update a record with the same companyCode
          },
        },
      });
    }

    if (params.action === 'delete') {
      return next({
        ...params,
        action: 'update',
        args: {
          ...params.args,
          data: {
            isDeleted: true,
          },
          where: {
            ...params.args.where,
            isDeleted: false,
            companyCode: this.getConnUserCompanyCode()//allow only to soft-delete a record with the same companyCode
          },
        },
      });
    }
    return next(params);
  };
© www.soinside.com 2019 - 2024. All rights reserved.