基于Hyperledger Fabric的医疗记录可信存证系统设计与实现 简介本资源是一套面向高校计算机及相关专业学生的毕业设计实战方案聚焦区块链技术在医疗数据安全存储中的落地应用适用于人工智能、通信工程、物联网等方向的本科生毕设、课程设计及初学者进阶学习。压缩包共202个文件含27个Java核心业务代码、12个YAML配置文件、36个PEM/16个CRT/14个私钥类证书文件支撑区块链节点身份认证与TLS加密通信以及43张系统流程与界面截图、5份XML配置与SQL建库脚本整体体积仅4.07MB结构紧凑、模块清晰。已有37人下载学习资源经严格测试可稳定运行配套完整设计文档、报告及环境部署说明支持远程指导与问题答疑。读者可直接复现基于Hyperledger Fabric或类似框架的医疗记录上链、权限控制与跨机构查询功能并在此基础上拓展智能合约或对接真实HIS系统。1. 医疗记录上链不是为了造个“区块链玩具”而是解决病历跨机构不可信、难追溯、易篡改的硬伤你手上有三甲医院的出院小结、社区卫生服务中心的慢病随访记录、体检中心的影像报告但它们彼此孤立格式不一调阅要盖章、等审批、跑窗口——这不是效率问题是数据主权和临床决策链断裂。毕业设计选题“基于区块链的医疗记录存储系统”核心不在炫技而在于用区块链的不可篡改性锚定记录生成时间与操作主体用分布式账本结构替代中心化HIS系统的单点风险让患者真正成为自己健康数据的控制者。它不替代电子病历系统EMR而是作为可信元数据层存哈希、留凭证、管授权不追求全量原始数据上链那会压垮性能而是聚焦关键事件签名、访问日志存证、授权变更轨迹。适合计算机或生物医学工程专业学生实操技术栈清晰Hyperledger Fabric 或 Ethereum 兼容链为主流、数据模型可收敛患者ID记录类型时间戳操作者哈希值、合规边界明确符合《个人信息保护法》对医疗健康信息的处理要求。本文将从链模型设计开始到本地部署验证再到真实医疗场景下的权限控制落地每一步都给出可运行命令、参数含义和失败排查点。2. 用 Hyperledger Fabric 搭建最小可行链5 个组织、3 个 Peer 节点、1 个排序服务的完整部署流程Fabric 是当前医疗行业落地最成熟的联盟链框架其通道Channel机制天然适配医院、疾控、医保等多主体协作场景而链码Chaincode的私有执行环境保障了患者隐私数据不出域。相比公链Fabric 不依赖挖矿交易确认快毫秒级且支持细粒度的 MSPMembership Service Provider身份管理——这正是医疗系统对“谁在什么角色下能读/写哪类记录”的刚性需求。2.1 环境准备与依赖安装Docker、Docker Compose、Go 与 Fabric Binaries 的版本锁定Fabric 对组件版本极其敏感必须严格匹配。以 v2.5.x 为例2024 年主流生产版本需安装# 安装 Docker≥20.10.0与 Docker Compose≥2.20.0 sudo apt update sudo apt install -y docker.io docker-compose sudo usermod -aG docker $USER newgrp docker # 安装 Go≥1.191.22Fabric v2.5 不兼容 Go 1.22 wget https://go.dev/dl/go1.21.10.linux-amd64.tar.gz sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf go1.21.10.linux-amd64.tar.gz echo export PATH$PATH:/usr/local/go/bin ~/.bashrc source ~/.bashrc # 下载 Fabric v2.5.4 二进制工具与镜像官方推荐方式 curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.5.4 1.5.4提示bash -s -- 2.5.4 1.5.4中的1.5.4是 Fabric CA 的版本号必须与 Fabric 主版本配套。若执行后提示docker: command not found说明 Docker 未正确加入用户组需重启终端或执行newgrp docker。2.2 配置网络拓扑定义 5 个组织医院A/B、社区中心、疾控、患者与通道策略医疗数据共享需明确责任主体。我们定义mednet通道要求任意一笔病历哈希上链操作必须获得至少 3 个组织签名如医院A 社区中心 患者本人防止单点伪造。配置文件network-config.yaml关键段如下organizations: HospitalA: mspid: HospitalAMSP peers: - peer0.hospitala.example.com certificateAuthorities: - ca.hospitala.example.com PatientOrg: mspid: PatientMSP admins: - ./crypto-config/peerOrganizations/patient.example.com/users/Adminpatient.example.com/msp/signcerts/cert.pem # ... 其他组织省略共5个 channels: mednet: orderers: - orderer.example.com peers: - peer0.hospitala.example.com - peer0.community.example.com - peer0.patient.example.com policies: Admins: type: Signature rule: OR(HospitalAMSP.admin, CommunityMSP.admin, PatientMSP.admin) Readers: type: Signature rule: OR(HospitalAMSP.member, CommunityMSP.member, PatientMSP.member) Writers: type: Signature rule: OR(HospitalAMSP.member, CommunityMSP.member, PatientMSP.member)注意policies中Readers和Writers规则使用OR表示任一成员即可读写但实际业务中应改为AND或更细粒度规则如“医院A写 患者读”此处为简化演示。真实部署中Admins策略应仅限于运维人员避免泛化。2.3 启动网络并验证节点状态用docker ps和peer channel list确认链已就绪执行启动脚本后检查容器是否全部运行cd fabric-samples/test-network ./network.sh up createChannel -c mednet -s couchdb # 等待约 90 秒后执行验证 docker ps | grep -E (peer|orderer|ca) | wc -l # 应输出 123 peer 1 orderer 5 ca 3 couchdb peer channel list -C mednet --peerAddresses peer0.hospitala.example.com:7051 --tlsRootCertFile ./organizations/peerOrganizations/hospitala.example.com/peers/peer0.hospitala.example.com/tls/ca.crt若peer channel list返回Channels peers joined: mednet说明通道创建成功。若报错error getting endorser client for channel: endorser client failed to connect to peer0.hospitala.example.com:7051: failed to create connection大概率是 TLS 证书路径错误或端口被占用需检查./organizations/peerOrganizations/hospitala.example.com/peers/peer0.hospitala.example.com/tls/ca.crt文件是否存在。3. 链码开发与部署用 Go 编写医疗记录存证合约实现哈希上链、授权查询、操作审计三功能链码Smart Contract是区块链的业务逻辑引擎。医疗记录上链的核心动作只有三个① 存入新记录的 SHA256 哈希值及元数据② 根据患者授权列表返回可访问的记录摘要③ 查询某条记录的所有操作日志谁、何时、做了什么。我们用 Go 编写一个精简版链码medrecord代码结构清晰无外部依赖。3.1 链码核心逻辑PutRecord、GetRecordByHash、GetAuditLog三个函数的实现要点链码主文件chaincode/medrecord/medrecord.go中关键函数如下// PutRecord 将医疗记录哈希、患者ID、记录类型、时间戳、操作者写入世界状态 func (t *SimpleChaincode) PutRecord(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) ! 5 { return shim.Error(Incorrect number of arguments. Expecting 5) } hash : args[0] // e.g., sha256:abc123... patientID : args[1] recordType : args[2] // lab_report, discharge_summary timestamp : args[3] // ISO8601 format operator : args[4] // MSP ID of the submitting org // 构建复合键record_patientID_timestamp key : record_ patientID _ timestamp recordJSON : map[string]string{ hash: hash, patientID: patientID, recordType: recordType, timestamp: timestamp, operator: operator, createdAt: time.Now().Format(time.RFC3339), } recordBytes, _ : json.Marshal(recordJSON) err : stub.PutState(key, recordBytes) if err ! nil { return shim.Error(fmt.Sprintf(Failed to put record: %s, err.Error())) } // 写入审计日志log_key logKey : log_ key logEntry : map[string]string{ action: PUT, by: operator, at: time.Now().Format(time.RFC3339), recordKey: key, } logBytes, _ : json.Marshal(logEntry) stub.PutState(logKey, logBytes) return shim.Success(nil) } // GetRecordByHash 仅返回哈希匹配的记录摘要不含原始数据 func (t *SimpleChaincode) GetRecordByHash(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) ! 1 { return shim.Error(Expecting 1 argument: hash) } hash : args[0] // 在世界状态中按哈希反向索引实际项目需建二级索引此处简化 resultsIterator, err : stub.GetStateByPartialCompositeKey(record, []string{}) if err ! nil { return shim.Error(err.Error()) } defer resultsIterator.Close() for resultsIterator.HasNext() { response, err : resultsIterator.Next() if err ! nil { continue } var record map[string]string json.Unmarshal(response.Value, record) if record[hash] hash { return shim.Success(response.Value) } } return shim.Error(Record not found) }逻辑说明PutRecord使用stub.PutState将结构化 JSON 写入 LevelDB默认状态数据库同时写入一条审计日志。GetRecordByHash采用全量扫描方式查找因 Fabric 不原生支持哈希索引实际生产环境需改用 CouchDB 并建立hash字段的视图索引。参数说明args[0]是记录哈希SHA256args[1]是患者唯一标识如身份证号脱敏后哈希args[2]是记录类型枚举值确保下游系统能按类型解析。3.2 部署链码到 mednet 通道指定背书策略为“医院A 患者”双签名部署时必须指定背书策略Endorsement Policy确保关键操作经多方确认。以下命令将链码安装到所有 Peer并在mednet通道上实例化要求交易必须获得HospitalAMSP和PatientMSP的签名# 进入 test-network 目录安装链码到各 Peer ./network.sh deployCC -c mednet -ccn medrecord -ccp ../chaincode/medrecord/ -ccl go -ccv 1.0 -cci init # 手动设置背书策略network.sh 默认为 OR需覆盖 peer chaincode invoke -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls true \ --cafile $ORDERER_CA \ -C mednet -n medrecord \ --peerAddresses peer0.hospitala.example.com:7051 \ --tlsRootCertFiles $PEER0_HOSPITALA_TLS_CA \ --peerAddresses peer0.patient.example.com:7051 \ --tlsRootCertFiles $PEER0_PATIENT_TLS_CA \ -c {function:init,Args:[]} # 设置背书策略关键 peer chaincode upgrade -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls true \ --cafile $ORDERER_CA \ -C mednet -n medrecord \ --peerAddresses peer0.hospitala.example.com:7051 \ --tlsRootCertFiles $PEER0_HOSPITALA_TLS_CA \ --peerAddresses peer0.patient.example.com:7051 \ --tlsRootCertFiles $PEER0_PATIENT_TLS_CA \ -c {function:init,Args:[]} \ -P AND(HospitalAMSP.member,PatientMSP.member)参数说明-P AND(HospitalAMSP.member,PatientMSP.member)是核心表示该链码所有交易必须由医院A的成员和患者组织的成员共同背书。若只写OR则单方即可提交失去多方制衡意义。$PEER0_HOSPITALA_TLS_CA等变量由test-network脚本自动导出无需手动设置。4. 模拟真实医疗场景患者授权社区中心查看高血压随访记录全程链上留痕毕业设计不能停留在“Hello World”级别。我们必须模拟一个闭环业务流患者通过 App 授权社区中心访问其近 3 个月的血压监测记录 → 社区医生发起查询 → 系统返回带签名的摘要 → 所有授权与查询动作写入区块链。这验证了系统在隐私、授权、审计三方面的有效性。4.1 构建患者授权链上凭证用Invoke函数写入grant_access记录授权不是口头约定而是链上不可逆的交易。我们扩展链码新增GrantAccess函数将授权关系存为独立状态func (t *SimpleChaincode) GrantAccess(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) ! 4 { return shim.Error(Expecting 4 args: patientID, granteeMSP, validUntil, purpose) } patientID : args[0] granteeMSP : args[1] // e.g., CommunityMSP validUntil : args[2] // 2024-12-31T23:59:59Z purpose : args[3] // hypertension_followup key : grant_ patientID _ granteeMSP grantJSON : map[string]string{ patientID: patientID, granteeMSP: granteeMSP, validUntil: validUntil, purpose: purpose, grantedAt: time.Now().Format(time.RFC3339), grantedBy: stub.GetCreator()[:20], // 简化取证书前20字节作ID } grantBytes, _ : json.Marshal(grantJSON) err : stub.PutState(key, grantBytes) if err ! nil { return shim.Error(err.Error()) } return shim.Success(nil) }调用命令由患者组织的 CLI 执行peer chaincode invoke -o localhost:7050 \ --ordererTLSHostnameOverride orderer.example.com \ --tls true \ --cafile $ORDERER_CA \ -C mednet -n medrecord \ --peerAddresses peer0.patient.example.com:7051 \ --tlsRootCertFiles $PEER0_PATIENT_TLS_CA \ -c {function:GrantAccess,Args:[PATIENT-123456,CommunityMSP,2024-12-31T23:59:59Z,hypertension_followup]}提示stub.GetCreator()返回的是提交交易的 MSP ID 的 DER 编码取前 20 字节可作简易操作者标识。真实系统应解析 X.509 证书提取 CN 字段如CNDr.Zhang,OUCommunity,DCexample,DCcom。4.2 社区中心发起合规查询先校验授权时效再读取记录摘要社区中心的 Peer 节点在查询前必须验证授权是否有效。这需在链码中实现CheckGrant函数并在GetRecordByPatient中调用func (t *SimpleChaincode) CheckGrant(stub shim.ChaincodeStubInterface, patientID, granteeMSP string) bool { key : grant_ patientID _ granteeMSP grantBytes : stub.GetState(key) if grantBytes nil { return false } var grant map[string]string json.Unmarshal(grantBytes, grant) if grant[validUntil] { return false } validUntil, _ : time.Parse(time.RFC3339, grant[validUntil]) return time.Now().Before(validUntil) } func (t *SimpleChaincode) GetRecordByPatient(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) ! 2 { return shim.Error(Expecting 2 args: patientID, recordType) } patientID : args[0] recordType : args[1] // 校验当前调用方社区中心是否有权访问 creatorMSP : stub.GetMSPID() // 返回 CommunityMSP if !t.CheckGrant(stub, patientID, creatorMSP) { return shim.Error(Access denied: no valid grant found) } // 查询该患者该类型的记录简化版实际需分页 resultsIterator, err : stub.GetStateByPartialCompositeKey(record, []string{patientID, recordType}) if err ! nil { return shim.Error(err.Error()) } defer resultsIterator.Close() var records []map[string]string for resultsIterator.HasNext() { response, _ : resultsIterator.Next() var record map[string]string json.Unmarshal(response.Value, record) records append(records, record) } resultBytes, _ : json.Marshal(records) return shim.Success(resultBytes) }调用命令由社区中心 Peer 执行peer chaincode query -C mednet -n medrecord \ --peerAddresses peer0.community.example.com:7051 \ --tlsRootCertFiles $PEER0_COMMUNITY_TLS_CA \ -c {function:GetRecordByPatient,Args:[PATIENT-123456,bp_monitoring]}若返回Access denied说明授权过期或不存在若返回 JSON 数组则证明授权链与查询链完全打通。5. 性能优化与合规落地CouchDB 索引加速查询、GDPR 右键删除支持、国密算法替换方案毕业设计要体现工程深度不能止步于功能可用。医疗系统对查询延迟敏感医生等不及对数据主权要求严苛患者有权要求删除对密码学合规有硬性规定国内需支持 SM2/SM3/SM4。以下三点是答辩时最易被追问的进阶细节。5.1 用 CouchDB 替代 LevelDB为hash和patientID字段建立 JSON 索引Fabric 默认的 LevelDB 不支持复杂查询当记录量超万条时GetRecordByHash全表扫描会超时。切换至 CouchDB键值对文档型混合数据库并建索引可将查询从秒级降至毫秒级// 在 CouchDB 中为 medrecord 数据库创建 design doc文件名design-records.json { _id: _design/records, views: { by_hash: { map: function(doc) { if (doc.hash) emit(doc.hash, doc); }, reduce: _count }, by_patient: { map: function(doc) { if (doc.patientID) emit([doc.patientID, doc.timestamp], doc); } } } }部署索引命令需先启用 CouchDB# 修改 docker-compose-test-net.yaml将 peer 的 environment 中 FABRIC_PEER_STATE_DATABASE 改为 CouchDB # 并添加 couchdb 服务官方 sample 已含 # 启动后用 curl 创建索引 curl -X PUT http://localhost:5984/medrecord/_design/records \ -H Content-Type: application/json \ -d design-records.json验证curl http://localhost:5984/medrecord/_design/records/_view/by_hash?key%22sha256:abc123...%22应返回匹配记录。索引后链码中GetRecordByHash可改用stub.GetQueryResult调用视图性能提升 10 倍以上。5.2 实现 GDPR “被遗忘权”用链下存储 链上注销的混合架构区块链不可删但 GDPR 要求“被遗忘”。解决方案是原始医疗数据存于受控的链下对象存储如 MinIO链上只存哈希与访问密钥当患者申请删除时链码执行RevokeAccess函数将密钥置为空并在链上写入注销日志。这样既满足法律要求又不破坏链的完整性func (t *SimpleChaincode) RevokeAccess(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) ! 2 { return shim.Error(Expecting 2 args: patientID, granteeMSP) } key : grant_ args[0] _ args[1] // 将原授权记录标记为 revoked grantBytes : stub.GetState(key) if grantBytes nil { return shim.Error(Grant not found) } var grant map[string]string json.Unmarshal(grantBytes, grant) grant[revokedAt] time.Now().Format(time.RFC3339) grant[status] revoked newBytes, _ : json.Marshal(grant) stub.PutState(key, newBytes) // 同时调用链下服务伪代码实际需集成 REST API // http.Post(http://minio-service/revoke-key, grant[accessKey]) return shim.Success(nil) }5.3 国密算法迁移路径用 fabric-ca-server 的--csr.cn参数注入 SM2 证书国内医疗系统必须支持国密。Fabric CA 支持签发 SM2 证书关键是在启动 CA 时指定 CSRCertificate Signing Request参数# 启动 CA 时添加国密参数 fabric-ca-server start \ --ca.certfile ca.crt \ --ca.keyfile ca.key \ --cfg.identities.allowremove true \ --csr.cn sm2-ca.example.com \ --csr.keyrequest.algo sm2 \ --csr.keyrequest.size 256 \ --tls.enabled true参数说明--csr.keyrequest.algo sm2强制 CA 签发 SM2 密钥--csr.cn设定证书主题名。后续所有组织注册时Fabric SDK 会自动使用 SM2 生成密钥对。注意Go SDK 需升级至hyperledger/fabric-sdk-go v2.2.12才支持 SM2旧版本会报错unknown algorithm。本文还有配套的精品资源点击获取