博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务
阅读量:6357 次
发布时间:2019-06-23

本文共 6921 字,大约阅读时间需要 23 分钟。

  权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务。

  权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量非常大,项目如果做了后端权限验证(其实为了项目数据的安全,必须每一个模块都需要做后端权限验证),那么每访问一个功能模块都会访问权限相关的数据。

  权限系统可能是集中管理的,即每一个不同的系统都需要访问权限中心数据,那这个访问量就会成倍的增加。

  我看到的很多权限控制都是直接读取数据库,这个带来的问题也很明显,性能较差,数据库压力大,包括Abp框架以及他的zero项目,数据库及项目性能都受权限系统拖累。

  基于以上原因,权限系统完全可以存储到缓存中。

Abp中的缓存

  Abp中是通过ICacheManager进行缓存管理了,他只是一个容器,真正的缓存是实现了ICache的类,Abp实现了两种缓存,一个是基于内存的AbpMemoryCache(Microsoft.Extensions.Caching.Memory. MemoryCache),一个是基于Redis的AbpRedisCache(使用StackExchange.Redis)

  单节点部署,直接用AbpMemoryCache即可,如果是分布式部署,则最好用Memcached实现,权限数据不需要持久化到物理介质,纯缓存。

缓存Key设计

  缓存Key格式定义为“AuthCenterService.实体名称.租户Id”,租户Id为空,固定写为-1,如果没有存储租户Id,则缓存整张表数据。代码示例:

public List
GetTenantSEC_Depts(int? tenantId) { string strTenant = tenantId.HasValue ? tenantId.Value.ToString() : "-1"; return _cache.Get($"{AuthCenterServiceModule.ModuleName}.{AuthCenterCacheConst.SEC_Dept}.{strTenant}", () => GetFromRepository(false,tenantId)); } public List
GetAllSEC_Depts() { return _cache.Get($"{AuthCenterServiceModule.ModuleName}.{AuthCenterCacheConst.SEC_Dept}.All", () => GetFromRepository(true)); } public List
GetFromRepository(bool isAll, int? tenantId = null) { UnitOfWorkOptions unitOfWorkOptions = new UnitOfWorkOptions(); unitOfWorkOptions.IsTransactional = false; unitOfWorkOptions.IsReadDb = true; using (var uow = _unitOfWorkManager.Begin(unitOfWorkOptions)) { if (isAll) { _unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant); var result = _repository.GetAllList(); uow.Complete(); return result; } else { _unitOfWorkManager.Current.SetTenantId(tenantId); var result = _repository.GetAllList(); uow.Complete(); return result; } } }

缓存Key的清除

  在权限数据有变换,或者有些情况人为的修改了权限数据,需要定义接口,删除对应的缓存,代码如下:

///         /// 清空租户部门缓存        ///         /// 租户Id        public void ClearSEC_DeptCache(int? tenantId)        {            string strTenant = tenantId.HasValue ? tenantId.Value.ToString() : "-1";            var cacheKey = $"{AuthCenterServiceModule.ModuleName}.{AuthCenterCacheConst.SEC_Dept}.{strTenant}";            _cache.Remove(cacheKey);        }///         /// 清空特定租户权限管理所有缓存        ///         public void ClearTenantAll(int? tenantId)        {            ClearSEC_AdminUserCache(tenantId);            ClearSEC_ModuleCache(tenantId);            ClearSEC_OperateCache(tenantId);            ClearSEC_RoleCache(tenantId);            ClearSEC_DeptCache(tenantId);            ClearSEC_ModuleSEC_RoleCache(tenantId);            ClearSEC_OperateSEC_RoleCache(tenantId);            ClearSEC_RoleSEC_AdminUserCache(tenantId);        }

  定义数据修改事件,页面修改了权限数据,触发事件,调用权限系统提供的服务,清除对应的缓存。

public class SEC_DeptEventHandler : IEventHandler
>, ISingletonDependency { private readonly IAbpSession _abpSession; public SEC_DeptEventHandler(IAbpSession abpSession) { _abpSession = abpSession; } public void HandleEvent(EntityChangedEventData
eventData) { Rpc.Call
("AuthCenterService.AuthCenterCacheAppService.ClearSEC_DeptCache", _abpSession.TenantId); } }

操作权限验证

  操作权限验证可能在用户的每一个动作都会触发,我这里的设计是用户在登录的时候,把用户的所有角色Id解析出来,存储到accesstoken信息里面(自定义Claims),修改Abp里面的IAbpSession接口,增加RoleIds字段(和UserId一样,从Claims里面读取),将每一个操作Code对应的角色Id集合存储到缓存里面,那么验证用户是否拥有某一个操作权限时,直接从缓存里面读取角色Id集合,判断用户拥有的角色Id是否包含在里面,达到验证目的。

public interface IAbpSession    {        // ......        ///         /// 存储用户角色Id集合,用于操作权限验证        ///         string RoleIds { get; }        ///         /// 用户请求的Token        ///         string AccessToken { get; }}

  在构造AccessToken的时候,添加RoleIds申明

public AuthenticateResultModel Authenticate(LoginResultModel loginResultModel)        {            List
claims = new List
(); claims.Add(new Claim(AbpClaimTypes.UserId, loginResultModel.UserId.ToString())); if (loginResultModel.TenantId.HasValue) { claims.Add(new Claim(AbpClaimTypes.TenantId, loginResultModel.TenantId.ToString())); } claims.Add(new Claim(AbpClaimTypes.RoleIds, loginResultModel.RoleIds)); // 添加用户角色申明 claims.Add(new Claim(AbpClaimTypes.UserName, loginResultModel.UserName)); var accessToken = CreateAccessToken(claims); return new AuthenticateResultModel { AccessToken = accessToken, EncryptedAccessToken = GetEncrpyedAccessToken(accessToken), ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds, UserId = loginResultModel.UserId }; }

  操作权限验证

public bool CheckAdminUserOperate(string roleIds, int? tenantId, string operateCode)        {            if (string.IsNullOrEmpty(roleIds))            {                return false;            }            var strOperateRoleIds = GetOperateRoleIdsFromCache(tenantId, operateCode);            if (string.IsNullOrEmpty(strOperateRoleIds))            {                return false;            }            strOperateRoleIds = ";" + strOperateRoleIds + ";";            var userRoleIds = roleIds.Split(new char[] { ';' });            return userRoleIds.Any(r => strOperateRoleIds.Contains($";{r};"));        }        private string GetOperateRoleIdsFromCache(int? tenantId, string operateCode)        {            string strTenant = tenantId.HasValue ? tenantId.Value.ToString() : "-1";            var cacheOperateRoleIdss = _cache.Get($"{AuthCenterServiceModule.ModuleName}.{AuthCenterCacheConst.OperateRoleIds}.{strTenant}"                         , () => GetOperateRoleIds());            if (!cacheOperateRoleIdss.ContainsKey(operateCode))            {                cacheOperateRoleIdss.Add(operateCode, GetOperateRoleIds(tenantId, operateCode));            }            return cacheOperateRoleIdss[operateCode];        }        private string GetOperateRoleIds(int? tenantId, string operateCode)        {            var tennantOperate = _sEC_OperateDomainService.GetTenantSEC_Operates(tenantId).FirstOrDefault(r => r.Code == operateCode);            if (tennantOperate == null)            {                return string.Empty;            }            return _sEC_OperateSEC_RoleDomainService.GetTenantSEC_OperateSEC_Roles(tenantId).Where(r => r.SEC_Operate_Id == tennantOperate.Id)             .Select(r => r.SEC_Role_Id.ToString()).Aggregate((r, t) => r + ";" + t);        }

 

转载于:https://www.cnblogs.com/spritekuang/p/10818282.html

你可能感兴趣的文章
flutter 教程(一)flutter介绍
查看>>
CSS面试题目及答案
查看>>
【从蛋壳到满天飞】JS 数据结构解析和算法实现-Arrays(数组)
查看>>
Spring自定义注解从入门到精通
查看>>
笔记本触摸板滑动事件导致连滑的解决方式
查看>>
Runtime 学习:消息传递
查看>>
你了解BFC吗?
查看>>
linux ssh tunnel使用
查看>>
十、详解FFplay音视频同步
查看>>
自定义元素探秘及构建可复用组件最佳实践
查看>>
小猿圈Python教程之全面解析@property的使用
查看>>
mpvue开发小程序所遇问题及h5转化方案
查看>>
View和Activity的生命周期
查看>>
解决PHP下载大文件失败,并限制下载速度
查看>>
Throwable是一个怎样的类?
查看>>
三条代码 搞定 python 生成验证码
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
无线和有线路由哪种性能更好
查看>>
Dwr3.0纯注解(纯Java Code配置)配置与应用浅析三之后端反向调用前端
查看>>