关于Abp Vnext 权限授权的问题

一.问题

最近收到一位朋友的求助,说他项目上的权限授权出现了问题,现象是在基础服务授权角色:RC 权限:X.Default,在基础服务使用RC角色的用户登录能访问到权限X.Default资源,而在X服务访问不到。重启X服务后就可以访问。

项目框架:ABP Vnext 6.0版本

数据库:共享一个

微服务架构如下:

关于Abp Vnext 权限授权的问题

 
 

请求/api/abp/application-configuration接口

基础服务

"auth": {     "Policies": {       "X.Default": true     }     "grantedPolicies": {      "X.Default": true     }   },

 X服务

"auth": {     "Policies": {       "X.Default": true     }     "grantedPolicies": {     }   },

 

二.分析原因

1.X服务权限资源已加载,没有获取到x.Default的授权

2.基础服务权限资源已加载并且获取到x.Default的授权

3.共享库AbpPermissionGrants表中存在记录:ProviderKey:R  ProviderName:RX  Name: x.Default。 说明该角色已经授权了X.Default

4.重启X服务后再次请求获取到X.Default的授权

由上可推测X服务获取不到X.Default授权的原因大概是因为缓存。

怎么验证猜测,把日志等级调为Debug,再次请求查看日志

2023-06-15 11:14:53.087 +08:00 [DBG] PermissionStore.GetCacheItemAsync: pn:RX,pk:R,n:X.Default......
2023-06-15 11:14:53.088 +08:00 [DBG] Found in the cache: pn:RX,pk:R,n:X.Default......

问题确定。

 

三.问题本质

要了解问题本质我们先来简单梳理一遍权限授权验证流程

关于Abp Vnext 权限授权的问题

不管是走中间件还是拦截器,验证授权最终都是调用了AbpAuthrizaionService.AuthorzieAsync()方法,结合日志,我们来看看PermissionStore.IsGrantedAsync()方法

public virtual async Task<bool> IsGrantedAsync(string name, string providerName, string providerKey)     {         return (await GetCacheItemAsync(name, providerName, providerKey)).IsGranted;     }      protected virtual async Task<PermissionGrantCacheItem> GetCacheItemAsync(         string name, // X.Default         string providerName,  // RX         string providerKey)   //R     {         var cacheKey = CalculateCacheKey(name, providerName, providerKey); //计算缓存key=pn:RX,pk:R,n:X.Default          Logger.LogDebug($"PermissionStore.GetCacheItemAsync: {cacheKey}");          var cacheItem = await Cache.GetAsync(cacheKey); //获取缓存          if (cacheItem != null)         {             Logger.LogDebug($"Found in the cache: {cacheKey}");             return cacheItem;   //存在则返回         }          Logger.LogDebug($"Not found in the cache: {cacheKey}");          cacheItem = new PermissionGrantCacheItem(false);          await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); //不存在缓存则查数据库后将结果缓存          return cacheItem;     }
 protected virtual async Task SetCacheItemsAsync(         string providerName,         string providerKey,         string currentName,         PermissionGrantCacheItem currentCacheItem)     {         var permissions = PermissionDefinitionManager.GetPermissions(); //获取该服务加载的权限资源          Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}");          var grantedPermissionsHashSet = new HashSet<string>(             (await PermissionGrantRepository.GetListAsync(providerName, providerKey)).Select(p => p.Name)  //从数据库查找已授权的权限资源         );             Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}");          var cacheItems = new List<KeyValuePair<string, PermissionGrantCacheItem>>(); //权限授权结果缓存集合          foreach (var permission in permissions)         {             var isGranted = grantedPermissionsHashSet.Contains(permission.Name); //存在授权列表中则已授权,否则未授权              cacheItems.Add(new KeyValuePair<string, PermissionGrantCacheItem>(   //把结果加进集合                 CalculateCacheKey(permission.Name, providerName, providerKey),                 new PermissionGrantCacheItem(isGranted))             );              if (permission.Name == currentName)             {                 currentCacheItem.IsGranted = isGranted;             }         }          await Cache.SetManyAsync(cacheItems); //设置缓存          Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}");     }

 第一次请求X服务:load了一遍权限资源,并把X.Default标记为false缓存了起来,后面再授权角色RX 资源X.Default,因为缓存的存在再次获取还是未授权。重启服务后正常

而基础服务之所以能实时更新是因为权限管理模型就在这里, PermissionGrantCacheItemInvalidator 订阅了PermissionGrant变更的本地事件会清空缓存。

 

四.解决方案

一.授权验证都走基础服务

1.引用Volo.Abp.AspNetCore.Mvc.Client nutget

2.添加AbpAspNetCoreMvcClientModule模块

3.添加配置

"RemoteServices": {     "AbpMvcClient": {       "BaseUrl": "http://localhost:XXXX", //配基础服务或网关     }   }

RemotePermissionChecker会取代PermissionChecker,请求远程服务进行权限验证,并将结果缓存起来,有效时间是300s(硬编码,todo:未来可配)

  configuration = await Cache.GetOrAddAsync(             cacheKey,             async () => await ApplicationConfigurationAppService.GetAsync(),             () => new DistributedCacheEntryOptions             {                 AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(300) //TODO: Should be configurable.             }         );

 

更改后的授权验证流程 

关于Abp Vnext 权限授权的问题

二 使用Redis缓存替换应用缓存

关于Abp Vnext 权限授权的问题 

 

备注:key要一致

 

最后朋友采用了方案二解决了问题,理由是实时。如果你有更好的解决方案请在评论告知我感谢!!!!

 

发表评论

评论已关闭。

相关文章