五一假期疫情封在家也没事做,就想来优化一下一个前端容器小项目

之前的TODOlist里面有一项是权限这块时隔2年了还一直没有动手

迟迟没搞主要还是我太懒了,哈哈 其实我一直想要找一个轻量级的权限通用方案

  • 权限的数据源可以切换,但是逻辑基本不用动
  • 权限策略定义简单不复杂,支持RBAC,ABAC(粒度可粗可细)
  • 支持内置超级用户(上帝模式)

知道我最近研究了一下casbin(基于各种访问控制模型的授权), 发现它正好满足了我以上几个点 官网: https://casbin.org/

基于cashbin的权限实践 1. 权限设计

分为2种权限:超级管理员(上帝模式) 和 普通用户

我这个程序的功能是按照项目维度来区分的,超级管理员创建一个空项目后,授权给别人去维护,总共包含7大功能:

超级管理员可以访问所有功能, 但只能是【超级管理员】做的有1和2和3

  • 1.权限配置(普通用户创建和删除,权限的修改和保存)
  • 2.全局配置
  • 3.创建空项目
  • 4.上传并部署该项目
  • 5.把项目回滚到上一个版本
  • 6.项目维度的服务端js脚本(读和写)
  • 7.项目维度的配置文件(读和写)

普通用户则可以被超级管理员在权限配置页面创建并进行配置来限制是否授予访问4~7这几个功能

2. 代码开发

casbin基本主流的开发语言都有对应的实现,这里我用netcore版本(Casbin.NET)

首先定义模型:


[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "root"

由于我这个是按照project进行权限控制的,所以我选用的是这个模型

  • sub -> user(登录用户名,root是超级用户/上帝模式)
  • obj -> project(项目)
  • act -> api资源(这里用了基于正则的方式为了应对配置一个用户可以访问project下所有权限)

/// <summary>
/// 创建casbin模型
/// </summary>
/// <returns></returns>
public static Enforcer createEnforcer()
{
    var e = new Enforcer();
    var m = NetCasbin.Model.Model.CreateDefault();
    m.AddDef("r", "r", "sub, obj, act");
    m.AddDef("p", "p", "sub, obj, act");
    m.AddDef("e", "e", "some(where (p.eft == allow))");
    m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "root"");
    var csv = Path.Combine(WebRootPath, CasBinPolicyFile);
    if (!File.Exists(csv))
    {
        File.CreateText(csv);
    }

    e.SetModel(m);
    // 目前我的权限配置文件是放在csv文件中 切换成存db的话 就切换一个adapter
    e.SetAdapter(new DefaultFileAdapter(csv));
    e.LoadPolicy();
    return e;
}

由于本身我的这个项目是一个中间件,


//内部api
app.UseWhen(
    c =>
    {
        // 检查路由是否满足要求
        if (!ApiMiddleware.CanInvoke(c, out var route))
        {
            return false;
        }
        // 路由规则满足后检查api是否存在
        return c.RequestServices.GetService<SpaDomain>()?.IsSpaApi(route.Item2) ?? false;
    },
    _ => _.UseMiddleware<ApiMiddleware>());

对于普通用户可访问的内部的api访问路径进行规则约束

  • 4.上传并部署该项目-> /{project}.reupload
  • 5.把项目回滚到上一个版本 -> /{project}.rollback
  • 6.项目维度的服务端js脚本(读) -> /{project}.getconfigjson
  • 项目维度的服务端js脚本(写) -> /{project}.saveconfigjson
  • 7.项目维度的配置文件(读) -> /{project}.serverjsget
  • 项目维度的配置文件(写)-> /{project}.serverjssave

这样我在ApiMiddleware里面可以进行统一权限拦截处理了

  • 解析请求路径 拿到 project(obj) 和 act (api)
  • 拿到当前登录 拿到 sub(user)
  • 拿到了sub,obj,act三要素后调用casbin方法进行验证
bool isAuthed = ef.Enforce(sub, obj, act);
设计一个页面来去配置策略

这也是针对casbin的一个ui操作的封装

  • 支持创建用户
  • casbin的策略进行增删改查
  • 支持的api资源的列表展示

那么通过这个ui操作就很容易去配置

粗粒度:某个用户对哪些project有权限

如上图,资源路径我配置了/* 代表这个zdyu用户可以访问project:test的所有操作

细力度:某个用户对哪些project的哪些具体操作有权限

如上图,代表zdyu这个用户只荀彧访问test这个project下的 部署和回滚2个功能

总结

本身研究怎么用casbin是非常简单的,这里主要分享了结合具体项目来如何设计,细节源码可以查看

https://github.com/yuzd/Spa

spa单页面容器里面一个project相当于一个二级域名的应用,应用内互相隔离,可以代替部署nginx或apache,对前端开发者友好,适合在某些只是用来静态项目访问的场景,来提高效率!