权限管控可以通俗的理解为权力限制,即不同的人由于拥有不同权力,他所看到的、能使用的可能不一样。对应到一个应用系统,其实就是一个用户可能拥有不同的数据权限(看到的)和操作权限(使用的)。
创新互联公司2013年成立,是专业互联网技术服务公司,拥有项目网站建设、网站制作网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元绛县做网站,已为上家服务,为绛县各地企业和个人服务,联系电话:18982081108
主流的权限模型主要分为以下五种:
目前主流的权限模型是RBAC模型,码猿慢病云管理系统则是使用RBAC模型进行权限控制。
关于以上5种权限模型在之前一篇文章中详细介绍过:权限系统就该这么设计,yyds
Role-Based Access Control,核心在于用户只和角色关联,而角色代表对了权限,是一系列权限的集合。
RBAC三要素:
在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。
角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。
角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系同样也存在继承关系防止越权。
优点:便于角色划分,更灵活的授权管理;最小颗粒度授权;
在码猿慢病云管理系统(医疗系统)的用户、角色、权限代表什么呢?
这里的用户和其他系统并无区别,则是能登录系统的用户,对应的表为:codepae/sys_user,字段属性如下:
字段 |
类型 |
注释 |
user_id |
bigint |
用户唯一ID |
name |
varchar |
姓名 |
gender |
char |
性别,字典 |
username |
varchar |
用户名/账号 |
password |
varchar |
密码 |
dept_id |
bigint |
科室ID |
hos_id |
bigint |
医院ID |
salt |
varchar |
随机盐 |
phone |
varchar |
手机号 |
avatar |
varchar |
头像 |
lock_flag |
char |
0-正常,9-锁定 |
del_flag |
char |
0-正常,1-删除 |
其中比较重要的字段:
为什么要有科室、病区这两个概念?
科室则是平常我们医院挂号经常看到的科室,比如妇科、骨科、内分泌科、心内科、心脑血管科等
病区这个概念是针对住院来说,住过院的应该都知道护士照顾病人是通过病区->病房->床位号定位病人,比如二十病区->302房间->10号床
医院为了病房能够更好的管理,节省医护的资源,一个病区中包含多个科室的患者,比如新生儿科、产科、儿科这三个科室你会看到里面的住院患者都是住在同一个病区,同一批护士管理,比如十病区。
这样应该就能理解了,大部分的HIS系统中,医生是划分到科室管理,比如妇科,骨科,毕竟术业有专攻,护士是按照病区划分管理,因为护士本质上是照顾病人,打打针等一些工作,专一性不是那么高;因此在sys_user用户表中的dept_id既能表示科室也能表示病区了。
PS:一些比较落后的小医院,HIS系统比较老旧,医生、护士还是按照科室管理
在医院中的主要角色则是:医生和护士,这个想必大家都能理解
码猿慢病云管理系统中内置了七个角色,已经完全够用了,如下:
图片
对应数据库:codeape/sys_role,如下:
字段 |
类型 |
注释 |
role_id |
bigint |
唯一Id |
role_name |
varchar |
角色名称 |
role_code |
varchar |
角色代码 |
role_desc |
varchar |
角色描述 |
del_flag |
char |
删除标识:0-正常,1-删除 |
和用户通过另外一张表存储关联关系:codeape/sys_user_role
字段 |
类型 |
注释 |
user_id |
bigint |
用户唯一ID |
role_id |
bigint |
角色唯一ID |
码猿慢病云管理系统中医生和护士这两个角色的最大区别:护士需要手持PDA(数据采集设备)采集数据(血糖、尿酸、血酮),添加数据等操作,医生则是每天查看患者的数据为治疗提供辅助
码猿慢病云管理系统中的权限有如下三类:
控制客户端的菜单显示,如下:
图片
目前有这几个根菜单+子菜单。
客户端按钮的权限,比如新增、删除、编辑按钮的权限,比如住院患者的4个按钮,如下:
图片
每个按钮都有一个权限标识编码,比如inhos_patinfohot_get,客户端只需要判断当前登录用户的权限树中是否存在这个权限,有则显示,没有则不显示
客户端的接口权限和按钮权限共用,比如查询住院患者这个权限对应的标识也是:inhos_patinfohot_get
那么这个接口权限如何控制?码猿慢病云管理系统中是将接口权限的鉴权下沉到各个微服务,交给开发者在开发接口时通过@PreAuthorize注解控制权限,比如查询住院患者列表的这个接口,如下:
//com.code.ape.codeape.inhos.controller.PatInfoHotController#getPatInfoHotPage
@Operation(summary = "分页查询在院患者", description = "分页查询在院患者")
@GetMapping("/page" )
@InjectAuth
@PreAuthorize("@pms.hasPermission('inhos_patinfohot_get')" )
public R> getPatInfoHotPage(Page page, PatInfoDTO dto) {
return R.ok(patInfoHotService.listPage(page,dto));
}
@PreAuthorize这个注解是Spring Security内置的鉴权接口,其中的value这个属性支持SPEL表达式,真实的实现代码如下:
/**
* @author 公众号:码猿技术专栏 版权:不才陈某所署,侵权必究
* {@link com.code.ape.codeape.common.security.component.PermissionService}
*/
public class PermissionService {
/**
* 判断接口是否有任意xxx,xxx权限
* @param permissions 权限
* @return {boolean}
*/
public boolean hasPermission(String... permissions) {
if (ArrayUtil.isEmpty(permissions)) {
return false;
}
//代码(1)
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
//代码(2)
Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
////代码(3)
return authorities.stream()
.map(GrantedAuthority::getAuthority)
.filter(StringUtils::hasText)
.anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
}
}
逻辑其实很简单,上述代码三个部分:
这个权限则是比较特殊了,先描述一下场景:
在医疗系统中,医生/护士是无权限查看全部科室的数据的,只能查看自己负责科室/病区的数据,这样是为了避免医疗事故;你想想如果有个心怀不轨的医生,随意更改其他科室/病区患者的数据,导致医生误判病情,这样的责任谁来承担?
所以医疗系统中都需要控制医护科室/病区这个权限,这样才能保证不发生不必要的医疗事故。
码猿慢病云管理系统中的科室/病区权限如何控制的呢?
在新增医护的时候有个科室权限的多选器,如下图:
图片
这样就能轻松设置医护的科室/病区权限了,这部分对应关系是持久化在codeape/sys_user_dept这张表中,如下:
字段 |
类型 |
注释 |
user_id |
bigint |
用户ID |
dept_id |
bigint |
科室/病区ID |
del_flag |
varchar |
删除标志 |
那么此时当前医护的科室/病区权限如何计算呢?
先给答案,分为两种情况;
先说第一种:在WEB端/PAD端,医护的权限=医护所在的科室+医护的科室权限+关联科室
什么意思?比如白鹿这个护士,如下图:
图片
所属科室为内分泌科,科室权限为神经内科+内分泌科+心内科,那么前面两层的权限则是神经内科+内分泌科+心内科(注意去重)
那么关联科室什么意思?比如神经内科下面有个神经内科病区,但是医护的科室在入职时都设置在了神经内科,那么他们如何能看到神经内科病区的数据呢?
一种方案可以在科室权限那一栏再加上神经内科病区,这种当然可行,但是你要为每个医护都设置一遍。
第二种方案:可以将神经内科和神经内科病区关联起来,这样只要有神经内科这个权限,那么就必然有神经内科病区这个权限,这个在码猿慢病云管理系统中也有设置关联关系,如下图:
图片
在添加科室的时候可以选择根节点科室。
对应的关系持久化在codeape/sys_dept_relation中,结构如下:
字段 |
类型 |
注释 |
ancestor |
bigint |
祖先节点(科室/病区ID) |
descendant |
bigint |
子节点(科室/病区ID) |
科室/病区权限在用户登录时就会查询出来放到SecurityContext上下文中,具体的方法如下图:
图片
那在医护查询数据如何去根据这个科室/病区权限过滤呢?
在请求DTO中有个基础实体类com.code.ape.codeape.common.core.entity.BaseParam,如下:
@Data
public class BaseParam {
/**
* 医院Id
*/
private Long hosId;
/**
* 用户ID
*/
private Long userId;
/**
* 医护的科室权限
*/
private List dataAuth;
/**
* 请求来源客户端ID
*/
private String clientId;
/**
* 设备的SN号
*/
private String sn;
}
这个类中的所有属性都会自动注入,只需要在controller方法中标注一个注解:@InjectAuth,如何做到的呢?
@InjectAuth这个注解是通过AOP方式自动注入参数,代码如下:
@Slf4j
@RequiredArgsConstructor
@Aspect
public class CodeapeInjectAuthAspect implements Ordered {
@SneakyThrows
@Around("@annotation(injectAuth) &&" +
"(@annotation(org.springframework.web.bind.annotation.PostMapping)||" +
"@annotation(org.springframework.web.bind.annotation.GetMapping)||" +
"@annotation(org.springframework.web.bind.annotation.DeleteMapping)||" +
"@annotation(org.springframework.web.bind.annotation.PutMapping)||" +
"@annotation(org.springframework.web.bind.annotation.RequestMapping))")
public Object around(ProceedingJoinPoint joinPoint, InjectAuth injectAuth) {
if (injectAuth.enable()){
CodeapeUser codeapeUser = Objects.requireNonNull(SecurityUtils.getUser());
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
if (!(args[i] instanceof BaseParam)) {
continue;
}
BaseParam baseParam = (BaseParam) args[i];
/**
* 1. web端:权限就是当前登录用户的权限
* 2. pda端,权限则是设备的权限+当前登录用户的权限
* 这里的权限取值是token中的deptAuths,这个在登录的时候就查询出来,缓存在redis中,所以这里直接用就可以
*/
//医院管理员+系统管理员不赋予权限
boolean flag= ArrayUtil.contains(codeapeUser.getRoleCodes(),SecurityConstants.SYSTEM_ADMIN_CODE)
||ArrayUtil.contains(codeapeUser.getRoleCodes(),SecurityConstants.HOS_ADMIN_CODE);
baseParam.setDataAuth(flag?null:Arrays.asList(codeapeUser.getDeptAuths()));
baseParam.setHosId(codeapeUser.getHosId());
baseParam.setUserId(codeapeUser.getId());
baseParam.setClientId(codeapeUser.getClientId());
baseParam.setSn(codeapeUser.getSn());
}
}
return joinPoint.proceed();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 4;
}
}
直接取的是CodeapeUser中的deptAuths,CodeapeUser则是SecurityContext上下文的用户身份信息
这样则能够取到用户的科室/病区权限,然后则能在SQL中根据这个dataAuth属性去过滤数据了,比如分页查询住院患者的接口,controller方法如下:
com.code.ape.codeape.inhos.controller.PatInfoHotController#getPatInfoHotPage
图片
图片
SQL如下:
com.code.ape.codeape.inhos.mapper.PatInfoHotMapper#selectPatInfoPage
图片
这部分的SQL片段则是根据住院患者的科室去过滤。
这里的PDA指的是护士的手持设备,这个设备和智能手机一样,根据自己账号登录上去,给大家大致画一下PDA上都有哪些内容,如下图:
图片
其实就和APP是一样的,里面可以看到大致四块内容(当然还有其他):
那么问题来了,PDA上需要显示的数据是整个医院的数据吗?显然不可能,也是需要根据护士科室/病区权限过滤,可以看到上方有一个科室的筛选项,默认是所有科室,则是当前登录用户的所有科室权限
那么PDA端的医护权限和WEB端一样吗?
当然不是,因为设备存在以下两种网络情况:
离线情况下就需要设备本地做缓存了,那么这个缓存的数据到底拉哪些数据?不可能将整个医院的数据都拉下来,设备的内存是有限的。这个时候就需要用到设备的权限了,在添加设备的时候有两个的选项,如下:
图片
科室和关联科室这两个选项,一般一台设备只供一个病区使用,此时将科室选项选择对应的病区即可,那么特殊情况下,比如一病区和二病区共用一台设备,此时就需要用到关联科室了。
此时应该明白了设备的权限=科室+关联科室
只要设备连上网络,在用户不登录的情况下,调用接口获取数据的时候就应该获取的是设备权限的数据。
那登录该台设备的用户权限呢?应该怎么取值呢?很简单,分为两种情况:
按照正常的逻辑是应该是第二种情况取交集,因为你护士没有这个权限就不应该看到该科室/病区的数据,但是实际情况是很多医院的护士都是轮转的,比如这个月到一病区,下个月到二病区,他们的科室/病区权限的维护并不是很及时,主要是信息科的人员太懒了,这样的话就会导致如果按照第二种情况取交集,那么这个护士登录该台设备就看不到自己所管的科室/病区的数据了。所以在码猿慢病云管理系统中是采用的第一种方案。
相关代码在com.code.ape.codeape.common.security.service.CodeapePDAUserDetailsServiceImpl#getUserDetails,如下图:
图片
这节内容介绍了RBAC权限模型以及码猿慢病云管理系统中权限是如何设计的,最重要的是科室/病区权限的设计,大家一定要理解其中的逻辑,几乎所有的医疗系统都是按照这个逻辑处理的。
本节内容摘录了部分代码,关于代码的详细介绍会在后面文章中介绍,现在先了解一下。
新闻标题:医疗系统的权限就该这样设计,稳!
网址分享:http://www.shufengxianlan.com/qtweb/news36/552536.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联