概述
INFO
权限执行生命周期和功能设计说明
AuthorBuilder
本类是整个执行的驱动类,承载了权限的整个执行过程,驱动各阶段的执行。其主要成员如下:
AuthDialect--dialect
抽象方言对象,用于处理权限的数据加载和解析部分,对应于各子类如RespLoader、AuthLoader等。
SQLBuilder--kit
用于构建SQL的工具,业务侧传入,以便直接织入权限SQL
WordBagDialect--wordBag
词条集合,同样方言化,便于业务侧进行个性化的词条定义。默认实现为 WordGroupBag,即分组词条。可以通过重写抽象工厂AuthFactory,以更换词条产品。
AuthUser--authUser
权限主体,用户。
roleIds
需要计算权限的角色ID
dataScopes
未解析的原始词条
AuthGoodsBag--goodsBag
过滤权限的资源集合
执行权限生成的过程
前置时刻
此时,主要需要初始化环境信息,如登录人,需要过滤词条范围的资源集合等。
登录人
.useDuty(_userManager, (duty) => {
duty....
})资源范围限定
duty.useMenu(Pageid) //设置要过滤的菜单
.useVCLink(entity.cellLinkOID)
.useLoginVisitBag(false) //禁用访客加载时的登录人过滤条件
.useManVisit(manCode) //当不使用登录人时,指定访客过滤人
.useOrgVisit(entity.KB_OrgOID)//指定访客过滤的单位
.useWordPara("{manCode}", manCode)加载词条
加载角色,调用loadDataScopes方法,从数据库读取权限主体拥有的词条,放入到 dataScopes中 加载完毕后,触发 AuthBuilder 中通过 onLoadedWords 注入的动作
public override List<AuthWord> loadDataScopes()
{
return dialect.loadRole(user, (kit) =>
{
if (goodsBag !=null && goodsBag.Goods.Count>0)
{
var codes = goodsBag.Goods.map((goo) => goo.code);
//带有BPO参数时,按照BPO进行过滤
kit.whereIn("r.UCML_RESPONSIBILITYOID", (e) =>
{ //传入了菜单的全面码时,使用权限码进行角色过滤。
e.select("m.UCML_RESPONSIBILITYOID").distinct()
.from("UCML_RESPONSIBILITYBPOMAP as m left join BusinessList as b on m.BusinessListOID = b.BusinessListOID ")
.whereIn("b.BPOID", codes)
.where("m.UCML_RESPONSIBILITYOID is not null");
});
}
if (_loadRoles != null)
{
_loadRoles(kit);
}
});
}空权限检查
检查词条是否为空,触发 AuthBuilder中通过 onEmpty 注入的动作 注入时如下:
resp.onEmpty((duty) => {
//没有配置权限时,可以查自己单位的。
kit.whereLikeLeft("b.Varchar1", myCode);
return "";
})解析词条
调用 AuthBuilder 中 readRoleDataScope 方法,放入到 wordBag 中, 一般此处会调用子方法readRole,然后readRole唤起 过滤器管道 PipelineDialect的readRoleScopeCode方法。
AuthBuilder需要进行重写,实现如下
public override void readRoleDataScope()
{
if (user.UserID.ToLower() == "admin")
{
//超级管理员,直接添加所有权限
wordBag.addAll();
return;
}
var au = CastToUser(user);
foreach (AuthWord roleScope in this.dataScopes)
{
this.readRole(roleScope, au);
}
}然后调用的过滤器管道
public class RespPipeline : PipelineDialect
{
public override int readRoleScopeCode(WordBagDialect range, AuthWord role, AuthUser user)
{
if (role.scopeCode == "4")//所有数据
{
range.addAll();
return 1;
}
else if (role.scopeCode == "28")//本管理员
{
var orgs = user.loadBindWorkOrg(role);
foreach (var org in orgs)
{
range.addBindOrg(org);
}
}
else if (role.scopeCode == "29")//本管理员
{
var orgs = user.loadBindWorkOrg(role);
foreach (var org in orgs)
{
range.addContainOrg(org);
}
}
else if (role.scopeCode == "3")//本部门及下级
{
range.addContainOrg(user.HisDivision);
}
else if (role.scopeCode == "2")//本部门
{
range.addBindOrg(user.HisDivision);
}
else if (role.scopeCode == "1")//本人数据
{
range.addMan(user);
}
else if (role.scopeCode == "7")//本单位及下级
{
range.addContainOrg(user.HisOrg);
}
else if (role.scopeCode == "6")//本单位
{
range.addBindOrg(user.HisOrg);
}
else if (role.scopeCode == "999")//没有岗位
{
var post = user.HisPost;
range.addBindPost(post);
}
else if (role.scopeCode == "999")//没有岗位
{
var post = user.HisPost;
range.addContainPost(post);
}
return 0;
}
}动态词条解析前时刻
调用 AuthBuilder中 onLoadWord 注入的数据范围加载方法,主要用于在动态词条解析前,放入动态词条所需的动态业务参数变量。
//调用词条加载完毕事件
private void invokeOnLoadedWords() {
if (_onAfterLoadWordActions != null) {
foreach (var action in _onAfterLoadWordActions)
{
action(dialect);
}
}
}动态词条解析
调用 wordTranslator 词条翻译器,进行词条解析,内置翻译器为 WordTranslator
resp.onBuildLiveWord((child, k) => {
if (child.key == "{taskBase}") {
child.ApplyToSQL(k);
}
else if (child.key == "{taskOrg}" && !string.IsNullOrEmpty(entity.KB_OrgOID))
{
kit.whereIn("a.KB_TaskOID", (org) =>
{
org.select("o.KB_Task_FK")
.from("KB_TaskOrgType o left join ucml_organize uo on uo.Varchar1 = o.KO_Code");
child.ApplyToSQL(org);
});
return;
}
else if (child.key == "{taskMan}" && !string.IsNullOrWhiteSpace(manCode))
{
kit.whereIn("a.KB_TaskOID", (man) =>
{
man.select("m.KB_Task_FK")
.from("KB_TaskManType m");
child.ApplyToSQL(man);
});
return;
}
})静态词条解析
调用 doWordBagBuild,触发 wordBag的 build 方法, 每个wordBag的build方法会调用其注册的 use系列、where系列解析方法,如默认的 useOrgIsField、whereOrgIs等,真正的将权限SQL织入到SQL中。
resp.useOrgIsField("a.D_OrgOID") //设置指定组织的字段
.useOrgLikeField("a.D_DeptCode") //指定组织及下级使用的字段最后的空检查
如果构造的权限为空,调用 dealEmptyAuth ,唤起 onEmpty ,或者 创建 “1=2”条件。