后端开发笔记:casbin 权限管理使用

May 12, 2024·
Sam.C
Sam.C
· 1 min read

casbin 是一个强大的权限管理工具。引入用于管理权限控制非常简单,对代码没有任何侵入,只要一个中间件,就可以实现。

我使用 eggjs prisma 做为后端开发的框架和数据库管理。在这个环境下要使用 casbin 需要用到 casbincasbin-prisma-adapter ,首先安装:

# NPM
npm install casbin --save

# Yarn
yarn add casbin

npm install casbin-prisma-adapter --save

在数据库中添加一个表格: casbin_rule 表,把以下代码加入 schema.prisma 文件,并执行 npx prisma db push 更新数据库和 PrismaClient 的数据结构:

model CasbinRule {
  id    Int     @id @default(autoincrement())
  ptype String
  v0    String?
  v1    String?
  v2    String?
  v3    String?
  v4    String?
  v5    String?

  @@map("casbin_rule")
}

修改 eggjs/app.js 文件,在应用启动阶段实例化 casbin 并把它挂载到 app 上,这样整个应用就只用一个 casbin 实例。代码中已经引入了 prisma casdoor

'use strict';
const { PrismaClient } = require('@prisma/client');
const { SDK } = require('casdoor-nodejs-sdk');
const { newEnforcer } = require('casbin');
const { PrismaAdapter } = require('casbin-prisma-adapter');

class AppBootHook {
  constructor(app) {
    this.app = app;
  }

  async willReady() { 
    const config = this.app.config.casdoorConfig;
    const prisma = new PrismaClient();
    this.app.casdoor = new SDK(config);
    const adapter = await PrismaAdapter.newAdapter(prisma);
    this.app.prisma = prisma;
    this.app.enforcer =await newEnforcer('./app/middleware/api_model.conf', adapter);
  }
}

module.exports = AppBootHook;

eggjs 的根目录是当前目录,所以这个位置读取可以这样写。

casbin-prisma-adapter 的库中可以看到其提供的 API,库的地址

然后实现中间件:

'use strict';
module.exports = () => {
    return async (ctx, next) => {
        if (ctx.path === '/api/signin') {
            await next();
            return;
        }

        try {
            const sub = ctx.state.user.name;
            const obj = ctx.request.url;
            const act = ctx.request.method
            const match = await ctx.app.enforcer.enforce(sub, obj, act)
            if (match === true) {
                console.log("验证通过,已放行。")
                await next();
            } else {
                ctx.status = 403;
                ctx.body = {
                    success: true,
                    message: '没有访问这些资源的权限。',
                };
            }
        } catch (error) {
            ctx.status = 500;
            ctx.body = {
                success: true,
                message: error,
            };
        }
    }
}