You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

4.7 KiB

统一手机号注册与首次身份选择设计

范围与基线

本设计面向后端实施、独立复验和前端联调。采用一次性身份选择,不实现双身份切换、管理员公开注册、历史账号转身份或线上部署。 开工分支 master,HEAD 8c9429108cbd5d85e5245f6c404b2172bf6dd7f3。暂存区空;已有 Phase B、验证码 P0、岗位等未提交变更及 290 个历史未跟踪条目须保留。

权威库存

  • AuthService.mockLogin 对新手机号显式创建 STUDENT;密码登录只登录既有账号。
  • User 主角色为 VARCHAR(30),user_roles 为关联表,无数据库枚举限制。最新迁移 V46,无新增字段需求。
  • JwtAuthenticationFilter 只建立已认证主体,业务角色由 Service 判定;存在仅要求认证的业务接口,需要入口白名单。
  • AuthSessionService.createSession/revokeAllUserSessions 使用 REQUIRED;刷新持锁顺序 users → auth_tokens,身份选择沿用该顺序。
  • AuthTokenRepository 的 modifying 操作 flush 并清空 persistence context;必须先 flush 角色变化,再撤销/创建会话。
  • 企业创建资料约束复用 CompanyRegistrationRequest.CompanyDetails;企业注册开关只接受字面量 true。
  • 现有 /auth/company/register 保留原契约,不新增第二套角色或企业认证状态。

状态机和兼容

新手机号通过 login 用途短信验证 → UNASSIGNED 主角色、空业务 roles → 一次选择 STUDENT 或 COMPANY。 COMPANY 选择同时创建 verified=false 的企业资料,后续沿用 Phase A 入驻与 Phase B 招聘认证门禁。 旧三种角色不迁移。实体/数据库 STUDENT 默认值暂保留用于既有内部创建点兼容,统一注册路径显式赋值,不依赖默认授权。 未知/矛盾主角色和 roles 组合不得通过身份选择;UNASSIGNED 即使被误写业务 roles 也应被公共角色 guard 拒绝。

接口

  • /auth/login、/auth/password/login 和 /auth/me 增加 identitySelectionRequired(主角色派生),保留其他字段和成功码。
  • POST /auth/identity/select:identity 仅 STUDENT/COMPANY;COMPANY 必须提供 company,STUDENT 禁止提供 company;身份来自当前会话,客户端不能指定 userId/role/verified。
  • 首次选择返回强类型响应:userId、role、identitySelectionRequired=false、companyId、onboardingStatus、新 Token 对及 expiresIn。
  • 有效会话重复相同身份不修改资料或审计,返回状态和 reauthenticationRequired=true,不再次颁发凭证。失效旧会话返回 401,重新登录可恢复。
  • 已选其他身份 409;企业开关关闭 503;未选择身份访问受限业务 403;非法请求 400。

白名单

待选身份仅允许 GET /auth/me、POST /auth/identity/select、POST /auth/logout、POST /auth/password/set/change,及既有匿名认证入口 /auth/login、/auth/password/login、/auth/password/reset、/auth/refresh、/auth/sms/send、/auth/company/register。 公开只读保持允许:GET /、/health(/子路径)、Swagger/OpenAPI、岗位公开列表/搜索/筛选/对比/评价、企业公开主页/岗位、岗位推荐分类字典。业务端口 actuator 路径保留原 404。 其余已登录业务统一拒绝(含资料、认证、投递、候选人、Offer、面试、消息、测评、通知、导出和管理员)。方法与路径同时匹配,不用 /auth/** 放行。 白名单检查由 IdentityAccessFilter 置于 JWT 验证之后的安全过滤链,在 MVC 路径/请求格式匹配之前执行,读取数据库当前身份;未知接口默认拒绝。通过禁用容器自动注册避免重复执行。公共角色 guard 同时拒绝 UNASSIGNED,避免服务调用用矛盾 roles 绕过。

原子事务与竞争

users 行锁 → 重新验证当前会话与账号注销状态 → 校验身份/资料/开关 → 更新角色并 flush → 建企业并 flush → 撤销全部旧会话 → 创建新会话 → 必需审计。任一失败回滚全部数据库更改,不返回 Token。 验证码错误计数独立事务及 Redis 限流不纳入该回滚范围,不改变既有 P0。 并发不同选择只允许一种提交,使用行锁后重新验证会话,防止认证过滤器校验与事务执行间的竞争。与刷新采用同一锁顺序;选择之前的会话在提交后失效。

验收

先保存生产修改前的失败证据,覆盖统一登录、身份选择、角色组合、企业开关、会话撤销、事务回滚和真实 MySQL 竞争。旧角色测试保留;仅更新明确依赖新手机号默认 STUDENT 的契约断言,不放宽授权断言。 H2 定向、认证关联、真实 MySQL、全量 test/clean verify、静态与运行时 OpenAPI 对齐后进入独立复验。真实短信未接通时明确受控 Mock,不冒充短信送达。