【.NET Core 3.1】 策略授权中获取权限数据

更多精彩推荐,上午11点到达

随着项目关注度渐渐升高,目前已经1.2k个star,我的内心反而更加的惶恐了起来,最近也是很有强迫症,只要有小伙伴反馈项目的问题,就很着急,哪怕一丁点的问题,就需要思考很久,生怕是底层设计的问题,导致项目有不可挽回的后果,那就断送了整个项目的发展了。

不过目前还好吧,除了数据库连接偶尔有人反馈说,在异步并发中,会间歇性的出现连接关闭的问题,其他没有发现什么。当然如果感觉这个ORM不好用,换成EFCore也是一样的,其他的功能还是很抗的住,无论是DI、Filter、AOP还是授权。

但是就在前两天,我在优化代码的时候,为了做压测,把所有的附加功能都关了,当然缓存AOP也关闭了:


当时是没有考虑很多,就把代码提交到了远程Github,没想到引发了一次疑案,很凑巧,刚刚提交上去,立刻就有一个小伙伴反应了问题,说报错了,然后开了一个bug:


具体的错误场景是这样的,其他页面很正常,怎么刷新都没事儿,唯独【权限分配】页面报错了:


其实说实话,很久之前有人断断续续的问过这个小问题,但是我一直没有复现出来,所有就没办法去修改,这次正好有一个小伙伴遇到了,我当时一想,肯定是他自己修改了什么,导致出错了,我下载下来测试一下,就知道了。
没想到真的报错了,当时瞬间就感觉慌了,代码逻辑肯定是没有问题的,毕竟是写了一年了,也有很多人在使用,那这种幽灵问题是为何,如果一个项目出现幽灵bug,那是很纠心又难受的,所以,我决定让自己冷静冷静,好好的检测检测,故事就开始了。

今天不会去讲解什么是JWT,什么是授权,什么是自定义复杂策略授权,这些基本概念,可以看我的视频或者文章,今天主要说说,在复杂策略授权中,遇到的小问题。

01

到底是哪里的问题?

我看了一下错误报告,是这样的:

大概意思就是,通过sqlsugar请求的时候,因为我是策略授权,所以在PermissionHandler中,增加动态从数据库获取角色和接口的映射关系,所以现在在请求的时候,报错reader已经关闭了,不能再重复访问了,我当时一脸蒙圈。不会是Sqlsugar有问题吧,这要是底层的问题,可就难受了。看着情况应该是重复使用实例对象了,但是我service层用的是autofac注入没有问题呀,而且也是scoped的。

现在是找到了问题所在,就是我们的策略授权中,使用了

await _roleModulePermissionServices.RoleModuleMaps();

来获取角色菜单关系的缘故,下边我们就是根据问题来找方案了。

02

如何解决这个问题?

当时我就思考着,为何之前没有遇到过,是因为之前我用的是AOP缓存,这样每次请求,其实请求的是缓存的数据,所以不会出现重复使用数据库的DataReader,那方案以就出现了,我也是建议小伙伴这么弄的:

这样确实没有问题了,毕竟第一次是请求,后边十分钟都是在调用的缓存数据,肯定不会出现重复使用,但是这么说,心里没有底气呀,感觉自己都不好意思看到这个答案,夜里久久不能寐,继续研究。

夜里突然想起来一个问题,既然出现了问题,就要从问题根本着手处理,到底为什么会重复调用同一个DataReader?那肯定有一个地方用单例了,但是我的Service层就是Scope呀,继续研究,发现了问题所在。

我虽然Service是Scope的,但是授权处理器PermissionHandler当时图省事儿,设置了单例!


所以,这也就是导致了我们的Service是同一个实例了,而且很凑巧的是,Admin项目中的【权限分配】页里,正好同时发起了两次请求,就报错了。

解决方案就是把PermissionHandler的单例注入,改成Scoped注入即可:

03

进一步深入优化。

上边的改好了以后,我就深入的想了想,在PermissionHandler处理程序中,既然要获取全部的用户菜单关系,而且还是单例的,那为啥每次都要请求一次呢?登录的时候,获取一次不就行了?

var data = await _roleModulePermissionServices.RoleModuleMaps();

我们删除PermissionHandler中的相关代码,把他们移动到了登录页,获取token的时候:

 var user = await _sysUserInfoServices.Query(d => d.uLoginName == name && d.uLoginPWD == pass);
 if (user.Count > 0)
 {
     var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass);
     //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
     var claims = new List<Claim> {
         new Claim(ClaimTypes.Name, name),
         new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().uID.ToString()),
         new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) };
     claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));

     // 就是这里!!!
     var data = await _roleModulePermissionServices.RoleModuleMaps();
     var list = (from item in data
                 where item.IsDeleted == false
                 orderby item.Id
                 select new PermissionItem
                 {
                     Url = item.Module?.LinkUrl,
                     Role = item.Role?.Name,
                 }).ToList();

     _requirement.Permissions = list;

     var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement);
     return new JsonResult(token);
 }

这样的话,我们登录的时候,更新一次,然后其他的时候,就不重复获取了,但是这样有个小小的问题,就是如果token有效,管理员在后端修改相应的菜单权限的话,就必须重新登录了,但是也无伤大雅,我已经在代码中注释。

那这样的话,我们就不用把PermissionHandler的依赖注入方式改成Scope了,这样也会每次都实例化,干脆还是改成单例,毕竟我们不用在授权处理程序中获取角色菜单关系了。

所以最后我的两个方案是:

1、在LoginController的登录api中,获取

_roleModulePermissionServices.RoleModuleMaps();

同时,注入的方式,改成Singleton;

2、还是在PermissionHandler中获取角色菜单Map,但是注入的方式一定要是Scope的。

04

打完收工

目前更新已经一天了,没有发现问题,有一个bug解决了。




????点击【阅读原文】查看Blog.Core开源项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值