ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# casbin-权限管理框架 [官方文档](https://casbin.org/docs/zh-CN/overview) thinkphp6:[think-casbin](https://github.com/php-casbin/think-casbin) [文档](https://www.kancloud.cn/oldlei/casbin/1289453) ## 什么是验证授权 > 授权(英语:Authorization)一般是指对信息安全或计算机安全相关的资源定义与授予访问权限,尤指访问控制。动词“授权”可指定义访问策略与接受访问。 `授权`作为名词,其代表的是在计算机系统中定义的资源访问权限。而`验证授权`就是验证计算机帐户是否有资源的访问权限。 举个栗子,假设现在有一本书`book1`,其拥有`read`, `write`的操作,那么我们可以先定义以下`授权`: 1. `alice`可以`read`书籍`book1` 2. `bob`可以`write`书籍`book1` 3. `bob`可以`read`书籍`book1` 现在来了一个用户`alice`她想`write`书籍`book1`,这时调用验证授权功能模块的接口,验证授权功能模块根据上述`授权`规则可以快速判断`alice`不可以`write`书籍`book1`;过一会儿又来了一个用户`bob`他想`write`书籍`book1`,这时调用验证授权系统的接口,验证授权系统根据上述`授权`规则可以快速判断`bob`可以`write`书籍`book1`。 可以看到`身份认证系统`强调地是安全可靠地得到计算机用户的身份信息,而`验证授权`强调地是根据计算机的身份信息、访问的资源、对资源的操作等给出一个Yes/No的答复。 ## 常用授权模型 ### ACL ### RBAC ### ABAC ## 实现权限验证 前面提到了多种不同的权限模型,要完全自研实现不同的权限模型还是挺麻烦的。幸好`casbin`出现,它将上述不同的模型抽象为自己的`PERM metamodel`,这个`PERM metamodel`只包括`Policy`, `Effect`, `Request`, `Matchers`,只通过这几个模型对象的组合即可实现上述提到的多种权限模型,如果业务上需要切换权限模型,也只需要配置一下`PERM metamodel`,并不需要大改权限模型相关的代码,这一点真的很赞!!! 一个最简单的`ACL`权限模型即可像下面这样定义: `acl_simple_model.conf` ``` <pre class="calibre10">``` # Request definition <span class="token3">[</span>request_definition<span class="token3">]</span> r <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act # Policy definition <span class="token3">[</span>policy_definition<span class="token3">]</span> p <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act # Policy effect <span class="token3">[</span>policy_effect<span class="token3">]</span> e <span class="token1">=</span> <span class="token4">some</span><span class="token3">(</span><span class="token4">where</span> <span class="token3">(</span>p<span class="token3">.</span>eft <span class="token1">==</span> allow<span class="token3">)</span><span class="token3">)</span> # Matchers <span class="token3">[</span>matchers<span class="token3">]</span> m <span class="token1">=</span> r<span class="token3">.</span>sub <span class="token1">==</span> p<span class="token3">.</span>sub <span class="token1">&&</span> r<span class="token3">.</span>obj <span class="token1">==</span> p<span class="token3">.</span>obj <span class="token1">&&</span> r<span class="token3">.</span>act <span class="token1">==</span> p<span class="token3">.</span>act ``` ``` 相应的授权规则可以像下面这样定义: `acl_simple_policy.csv` ``` <pre class="calibre10">``` p<span class="token3">,</span> alice<span class="token3">,</span> data1<span class="token3">,</span> read p<span class="token3">,</span> bob<span class="token3">,</span> data2<span class="token3">,</span> write ``` ``` 这意味着`alice`可以`read`资源`data1`;`bob`可以`write`资源`data2`。 写一个简单的程序就可以完成该权限验证: ``` <pre class="calibre10">``` <span class="token5">package</span> main <span class="token5">import</span> <span class="token3">(</span> <span class="token2">"fmt"</span> <span class="token2">"github.com/casbin/casbin/v2"</span> <span class="token3">)</span> func <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> e<span class="token3">,</span> _ <span class="token3">:</span><span class="token1">=</span> casbin<span class="token3">.</span><span class="token4">NewSyncedEnforcer</span><span class="token3">(</span><span class="token2">"acl_simple_model.conf"</span><span class="token3">,</span> <span class="token2">"acl_simple_policy.csv"</span><span class="token3">)</span> sub <span class="token3">:</span><span class="token1">=</span> <span class="token2">"alice"</span> <span class="token">// the user that wants to access a resource.</span> obj <span class="token3">:</span><span class="token1">=</span> <span class="token2">"data1"</span> <span class="token">// the resource that is going to be accessed.</span> act <span class="token3">:</span><span class="token1">=</span> <span class="token2">"read"</span> <span class="token">// the operation that the user performs on the resource.</span> <span class="token5">if</span> passed<span class="token3">,</span> _ <span class="token3">:</span><span class="token1">=</span> e<span class="token3">.</span><span class="token4">Enforce</span><span class="token3">(</span>sub<span class="token3">,</span> obj<span class="token3">,</span> act<span class="token3">)</span><span class="token3">;</span> passed <span class="token3">{</span> <span class="token">// permit alice to read data1</span> fmt<span class="token3">.</span><span class="token4">Println</span><span class="token3">(</span><span class="token2">"Enforce policy passed."</span><span class="token3">)</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> <span class="token">// deny the request, show an error</span> fmt<span class="token3">.</span><span class="token4">Println</span><span class="token3">(</span><span class="token2">"Enforce policy denied."</span><span class="token3">)</span> <span class="token3">}</span> <span class="token3">}</span> ``` ``` ## casbin模型详解 `casbin`官方其实已经提供了多种模型的定义及示例policy定义,见这里。而且为了便于用户理解诊断模型及policy,还给了个在线的editor,修改模型或policy时可以利用此工具。 从上面的示例可以看出基于`casbin`实现权限验证,代码很简单,但`casbin`的模型定义及policy定义初看还是挺复杂的,这样详细理解一下。 `casbin`的模型定义里会出现4个部分:`[request_definition]`,`[policy_definition]`,`[policy_effect]`, `[matchers]`。 其中`[request_definition]`描述的是访问请求的定义,如下面的定义将访问请求的三个参数分别映射为`r.sub`、`r.obj`、`r.act`(注意并不是所有的访问请求一定是3个参数): ``` <pre class="calibre10">``` <span class="token3">[</span>request_definition<span class="token3">]</span> r <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act ``` ``` 同理`[policy_definition]`描述的是授权policy的定义,如下面的定义将每条授权policy分别映射为`p.sub`、`p.obj`、`p.act`(注意并不是所有的授权policy一定是3个参数,也不是必须只有一条授权policy定义): ``` <pre class="calibre10">``` <span class="token3">[</span>policy_definition<span class="token3">]</span> p <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act ``` ``` `[matchers]`描述的是根据访问请求如何找到匹配的授权policy,如下面的定义将根据`r.sub`、`r.obj`、`r.act`、`p.sub`、`p.obj`、`p.act`找到完全匹配的授权policy: ``` <pre class="calibre10">``` <span class="token3">[</span>matchers<span class="token3">]</span> m <span class="token1">=</span> r<span class="token3">.</span>sub <span class="token1">==</span> p<span class="token3">.</span>sub <span class="token1">&&</span> r<span class="token3">.</span>obj <span class="token1">==</span> p<span class="token3">.</span>obj <span class="token1">&&</span> r<span class="token3">.</span>act <span class="token1">==</span> p<span class="token3">.</span>act ``` ``` 在写`[matchers]`规则是还可以使用一些内置或自定义函数,参考[这里的文档](https://casbin.org/docs/en/syntax-for-models#functions-in-matchers)。 最后`[policy_effect]`描述如果找到匹配的多条的授权policy,最终给出的验证授权结果,如下面的定义说明只要有一条匹配的授权策略其`eft`是`allow`,则最终给出的验证授权结果就是`允许`(注意每条授权policy默认的eft就是allow)。 ``` <pre class="calibre10">``` <span class="token3">[</span>policy_effect<span class="token3">]</span> e <span class="token1">=</span> <span class="token4">some</span><span class="token3">(</span><span class="token4">where</span> <span class="token3">(</span>p<span class="token3">.</span>eft <span class="token1">==</span> allow<span class="token3">)</span><span class="token3">)</span> ``` ``` 如果使用`RBAC`权限模型,可能还会使用`[role_definition]`,这个`[role_definition]`算是最复杂的了,其可以描述user-role之间的映射关系或resource-role之间的映射关系。这么说比较抽象,还是举例说明: 假设模型定义如下: ``` <pre class="calibre10">``` <span class="token3">[</span>request_definition<span class="token3">]</span> r <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act <span class="token3">[</span>policy_definition<span class="token3">]</span> p <span class="token1">=</span> sub<span class="token3">,</span> obj<span class="token3">,</span> act <span class="token3">[</span>role_definition<span class="token3">]</span> g <span class="token1">=</span> _<span class="token3">,</span> _ <span class="token3">[</span>policy_effect<span class="token3">]</span> e <span class="token1">=</span> <span class="token4">some</span><span class="token3">(</span><span class="token4">where</span> <span class="token3">(</span>p<span class="token3">.</span>eft <span class="token1">==</span> allow<span class="token3">)</span><span class="token3">)</span> <span class="token3">[</span>matchers<span class="token3">]</span> m <span class="token1">=</span> <span class="token4">g</span><span class="token3">(</span>r<span class="token3">.</span>sub<span class="token3">,</span> p<span class="token3">.</span>sub<span class="token3">)</span> <span class="token1">&&</span> r<span class="token3">.</span>obj <span class="token1">==</span> p<span class="token3">.</span>obj <span class="token1">&&</span> r<span class="token3">.</span>act <span class="token1">==</span> p<span class="token3">.</span>act ``` ``` 授权policy文件如下: ``` <pre class="calibre10">``` p<span class="token3">,</span> data2_admin<span class="token3">,</span> data2<span class="token3">,</span> read p<span class="token3">,</span> data2_admin<span class="token3">,</span> data2<span class="token3">,</span> write g<span class="token3">,</span> alice<span class="token3">,</span> data2_admin ``` ``` 现在收到了授权请求`alice, data2, read`,这时`r.sub`为`alice`,根据`g = _, _`及`g(r.sub, p.sub)`,我们可以得出对应的`p.sub`可以为`data2_admin`,接下来再根据`r.obj == p.obj && r.act == p.act`,最终找到匹配的授权policy规则为`p, data2_admin, data2, read`,最后根据`some(where (p.eft == allow))`规则,此时验证授权的结果就应该是`allow`。 这里`casbin`根据`r.sub`查找对应`p.sub`的过程还会考虑角色继承。考虑以下授权policy文件: ``` <pre class="calibre10">``` p<span class="token3">,</span> reader<span class="token3">,</span> data2<span class="token3">,</span> read p<span class="token3">,</span> writer<span class="token3">,</span> data2<span class="token3">,</span> write g<span class="token3">,</span> admin<span class="token3">,</span> reader g<span class="token3">,</span> admin<span class="token3">,</span> writer g<span class="token3">,</span> alice<span class="token3">,</span> admin ``` ``` 现在收到了授权请求`alice, data2, read`,这时`r.sub`为`alice`,根据`g = _, _`及`g(r.sub, p.sub)`,我们可以得出对应的`p.sub`可以为`admin`,`reader`,`writer`,接下来再根据`r.obj == p.obj && r.act == p.act`,最终找到匹配的授权policy规则为`p, reader, data2, read`,最后根据`some(where (p.eft == allow))`规则,此时验证授权的结果就应该是`allow`。 通过`[role_definition]`还可以定义resource-role之间的映射关系,见[示例](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_model.conf)。 `casbin`的模型大概就是上面描述的了,很多概念理解起来可能比较费劲,结合示例及在[editor](https://casbin.org/en/editor)上做些实验应该理解得更快一些。 ## casbin相关事项 1. `casbin`的模型定义及授权policy定义还可以选择保存在其它存储中,见[Model Storage](https://casbin.org/docs/en/model-storage)、[Policy Storage](https://casbin.org/docs/en/policy-storage)、[Adapters](https://casbin.org/docs/en/adapters)。 2. `casbin`的`Enforcer`对象还提供了一系列API接口管理授权policy规则,见[Management API](https://casbin.org/docs/en/management-api)、[RBAC API](https://casbin.org/docs/en/rbac-api)。 3. 可以修改授权policy规则,那么当多个验证授权服务分布式部署时,必然要考虑某个服务修改了授权规则后,其它服务示例必须进行规则的同步。`casbin`也考虑到了这个需求,提供了Watchers机制,用于在观察到授权规则发生变更时进行必要的回调,见[Watchers](https://casbin.org/docs/en/watchers)。 4. 在多线程环境下使用`Enforcer`对象的接口,必须使用`casbin.NewSyncedEnforcer`创建`Enforcer`,另外还支持授权policy`AutoLoad`特性,见[这里](https://casbin.org/docs/en/multi-threading)。 5. `casbin`默认是从授权policy文件中加载角色及角色的继承信息的,也可以从其它外部数据源获取这些信息,见[这里](https://casbin.org/docs/en/role-managers)。 ## casbin代码分析 `casbin`整体代码很简单,很多代码都是模型定义及授权policy定义加载的逻辑,关键代码只有一个方法[Enforce](https://github.com/casbin/casbin/blob/master/enforcer.go#L329),见下面: ``` <pre class="calibre10">``` <span class="token5">if</span> <span class="token1">!</span>e<span class="token3">.</span>enabled <span class="token3">{</span> <span class="token5">return</span> <span class="token6">true</span><span class="token3">,</span> nil <span class="token3">}</span> functions <span class="token3">:</span><span class="token1">=</span> <span class="token4">make</span><span class="token3">(</span>map<span class="token3">[</span>string<span class="token3">]</span>govaluate<span class="token3">.</span>ExpressionFunction<span class="token3">)</span> <span class="token5">for</span> key<span class="token3">,</span> <span class="token5">function</span> <span class="token3">:</span><span class="token1">=</span> range e<span class="token3">.</span>fm <span class="token3">{</span> functions<span class="token3">[</span>key<span class="token3">]</span> <span class="token1">=</span> <span class="token5">function</span> <span class="token3">}</span> <span class="token5">if</span> _<span class="token3">,</span> ok <span class="token3">:</span><span class="token1">=</span> e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"g"</span><span class="token3">]</span><span class="token3">;</span> ok <span class="token3">{</span> <span class="token5">for</span> key<span class="token3">,</span> ast <span class="token3">:</span><span class="token1">=</span> range e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"g"</span><span class="token3">]</span> <span class="token3">{</span> rm <span class="token3">:</span><span class="token1">=</span> ast<span class="token3">.</span><span class="token6">RM</span> functions<span class="token3">[</span>key<span class="token3">]</span> <span class="token1">=</span> util<span class="token3">.</span><span class="token4">GenerateGFunction</span><span class="token3">(</span>rm<span class="token3">)</span> <span class="token3">}</span> <span class="token3">}</span> expString <span class="token3">:</span><span class="token1">=</span> e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"m"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"m"</span><span class="token3">]</span><span class="token3">.</span>Value expression<span class="token3">,</span> err <span class="token3">:</span><span class="token1">=</span> govaluate<span class="token3">.</span><span class="token4">NewEvaluableExpressionWithFunctions</span><span class="token3">(</span>expString<span class="token3">,</span> functions<span class="token3">)</span> <span class="token5">if</span> err <span class="token1">!=</span> nil <span class="token3">{</span> <span class="token5">return</span> <span class="token6">false</span><span class="token3">,</span> err <span class="token3">}</span> rTokens <span class="token3">:</span><span class="token1">=</span> <span class="token4">make</span><span class="token3">(</span>map<span class="token3">[</span>string<span class="token3">]</span>int<span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span><span class="token3">)</span> <span class="token5">for</span> i<span class="token3">,</span> token <span class="token3">:</span><span class="token1">=</span> range e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">.</span>Tokens <span class="token3">{</span> rTokens<span class="token3">[</span>token<span class="token3">]</span> <span class="token1">=</span> i <span class="token3">}</span> pTokens <span class="token3">:</span><span class="token1">=</span> <span class="token4">make</span><span class="token3">(</span>map<span class="token3">[</span>string<span class="token3">]</span>int<span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span><span class="token3">)</span> <span class="token5">for</span> i<span class="token3">,</span> token <span class="token3">:</span><span class="token1">=</span> range e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Tokens <span class="token3">{</span> pTokens<span class="token3">[</span>token<span class="token3">]</span> <span class="token1">=</span> i <span class="token3">}</span> parameters <span class="token3">:</span><span class="token1">=</span> enforceParameters<span class="token3">{</span> rTokens<span class="token3">:</span> rTokens<span class="token3">,</span> rVals<span class="token3">:</span> rvals<span class="token3">,</span> pTokens<span class="token3">:</span> pTokens<span class="token3">,</span> <span class="token3">}</span> <span class="token5">if</span> policyLen <span class="token3">:</span><span class="token1">=</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Policy<span class="token3">)</span><span class="token3">;</span> policyLen <span class="token1">!=</span> <span class="token6">0</span> <span class="token3">{</span> policyEffects <span class="token1">=</span> <span class="token4">make</span><span class="token3">(</span><span class="token3">[</span><span class="token3">]</span>effect<span class="token3">.</span>Effect<span class="token3">,</span> policyLen<span class="token3">)</span> matcherResults <span class="token1">=</span> <span class="token4">make</span><span class="token3">(</span><span class="token3">[</span><span class="token3">]</span>float64<span class="token3">,</span> policyLen<span class="token3">)</span> <span class="token5">if</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span> <span class="token1">!=</span> <span class="token4">len</span><span class="token3">(</span>rvals<span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token6">false</span><span class="token3">,</span> errors<span class="token3">.</span><span class="token4">New</span><span class="token3">(</span> fmt<span class="token3">.</span><span class="token4">Sprintf</span><span class="token3">(</span> <span class="token2">"invalid request size: expected %d, got %d, rvals: %v"</span><span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"r"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span><span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>rvals<span class="token3">)</span><span class="token3">,</span> rvals<span class="token3">)</span><span class="token3">)</span> <span class="token3">}</span> <span class="token5">for</span> i<span class="token3">,</span> pvals <span class="token3">:</span><span class="token1">=</span> range e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Policy <span class="token3">{</span> <span class="token">// log.LogPrint("Policy Rule: ", pvals)</span> <span class="token5">if</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span> <span class="token1">!=</span> <span class="token4">len</span><span class="token3">(</span>pvals<span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token6">false</span><span class="token3">,</span> errors<span class="token3">.</span><span class="token4">New</span><span class="token3">(</span> fmt<span class="token3">.</span><span class="token4">Sprintf</span><span class="token3">(</span> <span class="token2">"invalid policy size: expected %d, got %d, pvals: %v"</span><span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"p"</span><span class="token3">]</span><span class="token3">.</span>Tokens<span class="token3">)</span><span class="token3">,</span> <span class="token4">len</span><span class="token3">(</span>pvals<span class="token3">)</span><span class="token3">,</span> pvals<span class="token3">)</span><span class="token3">)</span> <span class="token3">}</span> parameters<span class="token3">.</span>pVals <span class="token1">=</span> pvals result<span class="token3">,</span> err <span class="token3">:</span><span class="token1">=</span> expression<span class="token3">.</span><span class="token4">Eval</span><span class="token3">(</span>parameters<span class="token3">)</span> <span class="token">// log.LogPrint("Result: ", result)</span> <span class="token5">if</span> err <span class="token1">!=</span> nil <span class="token3">{</span> <span class="token5">return</span> <span class="token6">false</span><span class="token3">,</span> err <span class="token3">}</span> <span class="token5">switch</span> result <span class="token3">:</span><span class="token1">=</span> result<span class="token3">.</span><span class="token3">(</span>type<span class="token3">)</span> <span class="token3">{</span> <span class="token5">case</span> bool<span class="token3">:</span> <span class="token5">if</span> <span class="token1">!</span>result <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Indeterminate <span class="token5">continue</span> <span class="token3">}</span> <span class="token5">case</span> float64<span class="token3">:</span> <span class="token5">if</span> result <span class="token1">==</span> <span class="token6">0</span> <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Indeterminate <span class="token5">continue</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> matcherResults<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> result <span class="token3">}</span> <span class="token5">default</span><span class="token3">:</span> <span class="token5">return</span> <span class="token6">false</span><span class="token3">,</span> errors<span class="token3">.</span><span class="token4">New</span><span class="token3">(</span><span class="token2">"matcher result should be bool, int or float"</span><span class="token3">)</span> <span class="token3">}</span> <span class="token5">if</span> j<span class="token3">,</span> ok <span class="token3">:</span><span class="token1">=</span> parameters<span class="token3">.</span>pTokens<span class="token3">[</span><span class="token2">"p_eft"</span><span class="token3">]</span><span class="token3">;</span> ok <span class="token3">{</span> eft <span class="token3">:</span><span class="token1">=</span> parameters<span class="token3">.</span>pVals<span class="token3">[</span>j<span class="token3">]</span> <span class="token5">if</span> eft <span class="token1">==</span> <span class="token2">"allow"</span> <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Allow <span class="token3">}</span> <span class="token5">else</span> <span class="token5">if</span> eft <span class="token1">==</span> <span class="token2">"deny"</span> <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Deny <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Indeterminate <span class="token3">}</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> policyEffects<span class="token3">[</span>i<span class="token3">]</span> <span class="token1">=</span> effect<span class="token3">.</span>Allow <span class="token3">}</span> <span class="token5">if</span> e<span class="token3">.</span>model<span class="token3">[</span><span class="token2">"e"</span><span class="token3">]</span><span class="token3">[</span><span class="token2">"e"</span><span class="token3">]</span><span class="token3">.</span>Value <span class="token1">==</span> <span class="token2">"priority(p_eft) || deny"</span> <span class="token3">{</span> <span class="token5">break</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> ``` ``` 这个代码逻辑很清楚了,就是根据`[matchers]`、`[request_definition]`、`[policy_definition]`找到匹配的`[policy_definition]`,再根据`[policy_effect]`最后得出最终的验证授权结果。可以看到该处理逻辑里大量地遍历了`e.model["r"]["r"].Tokens`、`e.model["p"]["p"].Tokens`、`e.model["p"]["p"].Policy`,当授权policy规则条数较多时,估计性能不会太好。但官方给了个[性能测试报告](https://casbin.org/docs/en/benchmark),据说性能还可以,这个后面还须再验证下。 为了优化性能,其实是可以将验证授权操作的结果进行缓存,官方也提供了[CachedEnforcer](https://github.com/casbin/casbin/blob/master/enforcer_cached.go),目测逻辑没问题,如果确实遇到性能瓶颈,可以考虑采用。 ## 其它外部支援 一些开源爱好者为`casbin`贡献了[很多中间件组件](https://casbin.org/docs/en/middlewares),便于在多个编程语言中集成`casbin`进行权限验证。 还有一些开源爱好者为`casbin`贡献了[模型管理及授权策略管理的web前端](https://casbin.org/docs/en/admin-portal),如果觉得手工修改授权策略文件不直观的话,可以考虑采用。 还可以看到目前[很多开源项目](https://casbin.org/docs/en/adopters)的权限验证部分都是采用了`casbin`来实现的,例如[harbor里的rbac权限验证](https://github.com/goharbor/harbor/tree/master/src/common/rbac)。 还发现一个基于`casbin`实现的身份认证及验证授权服务,[这个例子](https://github.com/Soontao/go-simple-api-gateway)以后可以好好参考一下。