三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

2026 年山东大学软件学院创新项目实训博客(七)

2026 年山东大学软件学院创新项目实训博客(七)

一、工作进展

在测试中发现之前的 JWT 令牌无法提供身份 id 的识别,现进行修改。

本阶段主要完成了基于 JWT 的统一身份识别与数据归属校验后端能力:在短信/微信登录签发访问令牌后,将用户主键写入 JWT 的subject,通过全局过滤器在请求入口解析令牌并写入线程上下文;业务层通过JwtUserContextElderlyAccessService获取当前用户 id 与角色,在健康数据、用药计划、病历等接口上按「老人本人 / 家属已绑定老人」过滤读写范围,避免「只验登录、不验归属」导致的越权查询。

本人工作:

  • 梳理登录态与业务主键关系:JWTsub对应elderly/family表主键,userType区分ELDERLY/FAMILY,登录响应同步返回userIdaccessToken供前端持久化。

  • 实现JwtService.resolvePrincipal/resolveUserId,统一从Authorization: Bearer解析身份,避免各 Controller 重复手写 Bearer 截取与parseAccessToken

  • 新增JwtAuthFilter+JwtUserContext:除登录、发码、Swagger 等白名单路径外,默认要求有效 JWT;请求结束finally清理 ThreadLocal,防止线程池复用串用户。

  • 实现ElderlyAccessServiceElderlyScopedCrudSupport,将「当前用户能访问哪些老人 id」沉淀为可复用校验,并接入健康数据、疾病、病历、用药计划等多模块*ForCurrentUser()读写。


二、具体内容

策略说明: 身份以服务端验签后的 JWT 为唯一可信来源,不信任客户端单独传入的「我是谁」;用户 id 落在标准字段sub中,避免与业务表主键语义脱节。入口过滤器负责「是否登录」,Service 层负责「是否允许访问该老人数据」;家属账号通过elderly_family绑定关系扩展可访问范围,与老人端「仅本人」形成对称规则。触达与存储仍走现有 PostgreSQL + 短信/OSS 能力,本阶段不新增登录表,仅在既有completeSmsLogin流程上签发令牌。

本周新增/调整的主要代码内容如下:

  1. 登录与令牌

  • 签发:JwtServiceImpl.generateAccessTokenuserId写入subjectuserTypephoneopenId写入自定义 claims;短信登录AuthServiceImpl.completeSmsLogin返回SmsLoginResponse(含accessTokenuserIdelderlyId等)。

  • 解析:resolvePrincipal(authorization)校验 Bearer 格式 →parseAccessToken→ 封装JwtPrincipal(userId, userType, phone, openId)resolveUserId供仅需主键的场景直接调用。

  • 白名单:/api/auth/sms-login/api/auth/sms/send-code/api/auth/sms/send及 Swagger 路径不经过过滤器,其余接口无 token 返回 401。

  1. 请求上下文与全局过滤器

  • JwtAuthFilterOncePerRequestFilter,解析成功后JwtUserContext.set(principal)doFilter结束后clear()

  • JwtUserContext: 提供requireUserId()requireFamilyId()requireElderlyId()requirePrincipal(),供 Controller / Service 在同一次请求内获取已校验身份。

  • TraceIdFilter: 通过@Order保证 traceId 与 JWT 过滤器执行顺序清晰。

  1. 数据归属与业务接入

  • ElderlyAccessServiceassertCanAccessElderly(elderlyId)——老人仅本人,家属仅bindStatus=1的绑定老人;listAccessibleElderlyIds()供列表查询IN过滤;resolveWritableElderlyId()写入时老人强制本人 id、家属须显式传elderlyId

  • ElderlyScopedCrudSupport: 封装带elderlyId实体的 list/get/add/update/delete 权限模板;MedicationScheduleTimemedication_plan间接关联老人 id 单独处理。

  • Controller 改造: 原service.list()/getById()改为listForCurrentUser()/getForCurrentUser(id)等;AuthController绑定老人接口从JwtUserContext.requireFamilyId()取家属 id,不再依赖重复解析 Header 的私有方法。

  1. 与现有业务的衔接

  • OSS 病历上传: 仍限制老人账号申请 STS(OssStsController+requireElderlyId()),与「用户 id = JWT subject」一致。

  • Agent / 会话模块: 为与master合并顺利,聊天会话相关 4 个文件暂对齐主分支实现;JWT 全链路鉴权与健康、用药等 CRUD 权限已保留,会话归属校验留待与 DB 版会话表统一后迭代。

  • 前端约定: 登录后存accessToken,业务请求带Authorization: Bearer <token>;家属操作老人数据需传elderlyId;401 引导重新登录。


三、问题与处理

问题处理
仅部分接口手写解析 JWT,大部分「查信息」不知道当前用户增加 JwtAuthFilter 默认鉴权 + JwtUserContext,统一入口确立身份
用户 id 在 JWT 里如何取、是否有 userId 字段使用标准 sub 作为用户主键;resolvePrincipal 统一封装,登录响应额外返回 userId 便于前端展示
家属能否访问未绑定老人健康数据ElderlyAccessService 查 elderly_family 绑定关系,无绑定 403
合并 master 时 PR 不可自动合并4 个 Agent/会话文件恢复为 master 版本;删除跟踪的 .idea/ 并与主分支 .gitignore 对齐,消除 modify/delete 冲突
ElderlyAccessService 与 ElderlyFamilyService 循环依赖绑定关系查询改为注入 ElderlyFamilyMapper,打破 Service 环依赖

四、下周计划

  • 与前端完成登录态联调:统一请求封装、token 刷新/过期处理与 401 跳转策略。

  • master合并 JWT 能力后,将会话模块(DB 持久化)与ownerUserId归属校验对齐,避免会话列表越权。

  • 完成项目最终联调。

← 返回列表