這是基于筆者的一些經驗設計并加以完善的方案,僅供參考。
處方流轉平臺權限控制模塊設計(基于RBAC模型)
1. 需求分析
處方流轉平臺需要嚴格的權限控制,確保:
- 患者隱私數據保護
- 處方開具、審核、調配、發藥等流程的合規性
- 不同角色(醫生、藥師、管理員等)的操作權限隔離
- 符合醫療行業法規要求(如HIPAA、GDPR等)
2. RBAC模型設計
2.1 核心組件
用戶(User) ← 用戶-角色關聯 → 角色(Role) ← 角色-權限關聯 → 權限(Permission)
2.2 數據庫設計
-- 用戶表
CREATE TABLE users (user_id VARCHAR(36) PRIMARY KEY,username VARCHAR(50) NOT NULL UNIQUE,password_hash VARCHAR(255) NOT NULL,real_name VARCHAR(100),department_id VARCHAR(36),is_active BOOLEAN DEFAULT TRUE,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 角色表
CREATE TABLE roles (role_id VARCHAR(36) PRIMARY KEY,role_name VARCHAR(50) NOT NULL UNIQUE,description TEXT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 權限表
CREATE TABLE permissions (permission_id VARCHAR(36) PRIMARY KEY,permission_code VARCHAR(100) NOT NULL UNIQUE,permission_name VARCHAR(100) NOT NULL,resource_type VARCHAR(50) NOT NULL, -- 如"處方"、"患者"、"藥品"等action VARCHAR(50) NOT NULL, -- 如"create","read","update","delete"description TEXT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 用戶-角色關聯表
CREATE TABLE user_roles (user_id VARCHAR(36) NOT NULL,role_id VARCHAR(36) NOT NULL,PRIMARY KEY (user_id, role_id),FOREIGN KEY (user_id) REFERENCES users(user_id),FOREIGN KEY (role_id) REFERENCES roles(role_id)
);-- 角色-權限關聯表
CREATE TABLE role_permissions (role_id VARCHAR(36) NOT NULL,permission_id VARCHAR(36) NOT NULL,PRIMARY KEY (role_id, permission_id),FOREIGN KEY (role_id) REFERENCES roles(role_id),FOREIGN KEY (permission_id) REFERENCES permissions(permission_id)
);
2.3 典型角色定義
-
醫生(Doctor)
- 權限:處方創建、修改(限未審核狀態)、查看本人開具處方
- 特殊約束:只能開具所屬科室的藥品
-
藥師(Pharmacist)
- 權限:處方審核、調配、發藥、查看所有處方
- 特殊約束:調配需雙人審核
-
藥房管理員(PharmacyAdmin)
- 權限:藥品庫存管理、處方統計查看
- 特殊約束:無藥品調配權限
-
系統管理員(SystemAdmin)
- 權限:用戶管理、角色管理、權限配置
- 特殊約束:無醫療業務操作權限
-
患者(Patient)
- 權限:查看本人處方、處方流轉狀態查詢
- 特殊約束:僅能訪問本人數據
3. 權限控制實現
3.1 權限驗證流程
1. 用戶登錄 → 獲取JWT令牌(包含用戶ID和角色)
2. 每次API請求攜帶JWT
3. 權限攔截器:a. 解析JWT獲取用戶角色b. 查詢角色對應的權限集c. 驗證當前請求資源+操作是否在權限集中
4. 通過 → 繼續處理; 拒絕 → 返回403
3.2 代碼實現示例(Java Spring Boot)
// 自定義權限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {String resource();String action();
}// 權限攔截器
@Component
public class PermissionInterceptor implements HandlerInterceptor {@Autowiredprivate PermissionService permissionService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 獲取當前用戶角色Authentication auth = SecurityContextHolder.getContext().getAuthentication();String roleId = auth.getPrincipal().getRoleId();// 2. 獲取請求資源和方法HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();String requestUri = request.getRequestURI();// 3. 檢查方法級權限注解if (method.isAnnotationPresent(RequiredPermission.class)) {RequiredPermission permission = method.getAnnotation(RequiredPermission.class);if (!permissionService.checkPermission(roleId, permission.resource(), permission.action())) {throw new AccessDeniedException("權限不足");}}// 4. 額外業務規則檢查(如數據歸屬)checkDataOwnership(auth, request);return true;}private void checkDataOwnership(Authentication auth, HttpServletRequest request) {// 示例:驗證患者只能訪問自己的處方if (request.getRequestURI().startsWith("/api/prescriptions") && "PATIENT".equals(auth.getRole())) {String prescriptionId = request.getParameter("id");if (!prescriptionService.isOwnedByPatient(prescriptionId, auth.getUserId())) {throw new AccessDeniedException("無權訪問該資源");}}}
}
3.3 權限服務實現
@Service
public class PermissionServiceImpl implements PermissionService {@Autowiredprivate RolePermissionMapper rolePermissionMapper;@Overridepublic boolean checkPermission(String roleId, String resource, String action) {// 1. 查詢角色所有權限List<Permission> permissions = rolePermissionMapper.findByRoleId(roleId);// 2. 檢查是否包含所需權限return permissions.stream().anyMatch(p -> p.getResourceType().equals(resource) && p.getAction().equals(action));}@Overridepublic Set<String> getUserPermissions(String userId) {// 獲取用戶所有權限(用于前端動態菜單)return rolePermissionMapper.findPermissionsByUserId(userId).stream().map(p -> p.getResourceType() + ":" + p.getAction()).collect(Collectors.toSet());}
}
4. 高級功能設計
4.1 動態權限管理
- 提供管理界面進行角色權限配置
- 權限變更實時生效(通過權限緩存刷新機制)
- 權限變更審計日志
4.2 數據級權限控制
除常規RBAC外,增加數據過濾規則:
// 在數據訪問層自動添加過濾條件
@Repository
public class PrescriptionRepositoryImpl implements PrescriptionRepositoryCustom {@Autowiredprivate SecurityContext securityContext;@Overridepublic List<Prescription> findAccessiblePrescriptions() {CriteriaBuilder cb = entityManager.getCriteriaBuilder();CriteriaQuery<Prescription> query = cb.createQuery(Prescription.class);Root<Prescription> root = query.from(Prescription.class);// 根據用戶角色添加不同過濾條件if (securityContext.isDoctor()) {query.where(cb.equal(root.get("creatorId"), securityContext.getUserId()));} else if (securityContext.isPharmacist()) {// 藥師可查看所有處方} else if (securityContext.isPatient()) {query.where(cb.equal(root.get("patientId"), securityContext.getUserId()));}return entityManager.createQuery(query).getResultList();}
}
4.3 權限繼承與組合
- 支持角色繼承(子角色繼承父角色所有權限)
- 支持權限組(將常用權限組合打包)
5. 安全增強措施
- 權限變更審計:記錄所有權限配置變更
- 定期權限復核:強制要求定期檢查用戶權限分配
- 敏感操作二次認證:如處方刪除需短信驗證
- 最小權限原則:新用戶默認無權限,需顯式分配
- 會話管理:支持強制下線、會話超時
6. 性能優化
- 權限緩存:使用Redis緩存用戶權限集,設置合理過期時間
- 批量檢查:對批量操作進行優化,避免N+1查詢問題
- 權限索引:確保權限相關查詢字段有適當索引
總結
處方流轉平臺的權限控制模塊設計應:
- 嚴格遵循RBAC模型,實現角色與權限的解耦
- 結合醫療行業特點,設計符合法規要求的權限規則
- 實現方法級和數據級的雙重權限控制
- 提供靈活的權限管理界面供管理員使用
- 確保系統性能的同時不降低安全性
這種設計可以滿足處方流轉平臺復雜的權限控制需求,同時保持系統的可維護性和擴展性。