第一阶段实现->访问控制层(第二层)
2026/8/4 22:07:43 网站建设 项目流程

核心策略(第二层,现行实现)

第二层访问控制可概括为:

  • challengeCode:服务端有状态生命周期(Redis TTL)
  • 请求级校验:时间戳窗口 + HMAC 签名(签名材料包含挑战上下文)
  • 短期反重放:RedisNX去重锁(按请求/按下载分片维度)
  • 资源访问收敛:通过authUuid查询下载对象与业务资格(DB 校验)

整体形态属于典型的stateful challenge:挑战码本身不自包含授权语义,关键语义与控制力体现在服务端 Redis 状态中(区别于 JWT 式自包含凭证)。

与第一层的衔接断层(现状)

第一层(TLS/mTLS)与第二层(访问控制)的衔接点体现在“业务侧信任的输入”。目前业务接口主要以X-VIN / X-Signature / X-Timestamp作为鉴权输入,业务代码未直接消费或绑定 mTLS 证书身份。

第一层输出与第二层输入的关系(现状表)

第一层输出

第二层输入

现状问题/风险点

mTLS 验证通过

业务侧主体标识仍主要来自X-VIN

风险点:业务侧未显式将“证书身份”与X-VIN做绑定校验;若网关到业务之间的信任边界不够强,X-VIN可能成为可伪造输入

TLS 会话建立

通道安全

通道安全不等于“请求合法”;现状依赖请求级timestamp + signature + challengeCode证明应用层合法性

设备主体识别

X-VIN(代码变量名多为deviceUuid

“是否具备升级资格”并非仅由 VIN 决定,仍需通过authUuid查询业务资格并绑定到唯一下载对象

控制器入口层:挑战码签发、下载、版本查询均要求X-VIN / X-Signature / X-Timestamp,入口在OTADownloadSoftwareController

Challenge Code(生命周期与交互)

设计意图

现状的challengeCode不是替代 JWT 式 token 做“无状态授权”,而是用于构建短期可控的动态授权上下文:

  • 服务端签发短期随机值,使后续签名升级为“动态上下文签名”
  • Redis TTL 提供可控时效性
  • Lua 脚本 +NX锁将“签发”和“反重放”原子化处理
接口与交互(现状)

现状的挑战签发接口为:

  • POST /ota/challenge/send
  • 请求头:X-VINX-SignatureX-Timestamp
  • 响应体:challengeCode+status=pending
  • 入口:OTADownloadSoftwareController
  • 实现:softwareChallengeSend

与“目标演进版”对齐对比(现状没有的):

  • otp_seed
  • idempotency_key(显式字段)
  • 权限清单/constraints
挑战码生成与存储(现状)

生成:

  • %06d六位随机数(伪随机),见softwareChallengeSend
  • Redis key 前缀:ota:c:KEY_CH),见OTADownloadSoftwareServiceImpl

写入:

  • 采用 Lua 脚本原子执行(脚本见RedisLuaConfig
    • KEYS[1]:反重放锁 key(示例:ok:s:{signature}
    • KEYS[2]:挑战码 key(ota:c:{deviceUuid}
    • 锁 TTL:现行传入120s
    • 挑战码 TTL:现行传入3600s

读取:

  • challengeFetchOnlyScript校验 key 存在并GET,不存在返回-1
  • 业务侧映射为CHALLENGE_EXPIRED,见fetchChallengeOnly
下载过程中的挑战码“续期”(现状)

下载接口在“首次下载(非续传 Range)”时会对ota:c:{deviceUuid}延长有效期(当前已调整为2h),逻辑位于downloadSoftwareStorage

  • 续传判定:range.start > 0认为续传(isSequel
  • HEAD且非续传时:expire(KEY_CH + deviceUuid, Duration.ofHours(2))

结论(现状行为边界):

  • 下载开始会续期
  • 断点续传不会续期
  • HEAD请求不会触发续期

请求级授权:时间戳窗口 + HMAC 签名 + 反重放(现状)

现状的“请求级授权”由三部分协作完成:时间戳窗口校验、HMAC 签名校验、Redis 短期反重放锁。

时间戳窗口(现状)
  • 校验规则:abs(now - reqTime) <= windowSec
  • 多个入口windowSec = 60s
  • 代码见isTimestampValid
HMAC 签名校验(现状)

签名消息拼装(Java fallback 逻辑):

  • parameter != nullmessage = deviceUuid + ":" + parameter
  • 否则:message = deviceUuid
  • 算法:HmacSHA256,结果 Base64
  • 代码见verifyHmacSignature

签名实现路径:

  • NativeSecurity优先,失败回退Java HMAC,见verifyHmacWithFallback

密钥/阶段(现状体现为“双阶段校验风格”):

  • challenge 签发阶段:使用challengeKey,参数timestamp
    • 绑定:deviceUuid + timestamp(见softwareChallengeSend
  • 后续业务阶段:使用sharedChallengeKey,参数challengeCode:timestamp
    • 绑定:deviceUuid + challengeCode + timestamp(见downloadInitialVerificationsoftwareAllVersionInformation
反重放 / 幂等(现状)

现状未提供显式idempotency_key字段,依赖 RedisNX锁实现短窗口去重/反重放。锁 key 前缀见LockKey

  • ok:s:(challenge send)
  • ok:g:(version info)
  • ok:d:(download)

典型行为(现状):

  • challenge send:Lua 脚本中对ok:s:{signature}NX + EX(120s),失败报REPLAY_ATTACK_DETECTED,见RedisLuaConfig
  • version info:ok:g:{signature}setIfAbsent(..., 2 min),失败判重放,见softwareAllVersionInformation
  • download:ok:d:{signature}:{rangePart}setIfAbsent(..., 2 min),失败判重放,见downloadSoftwareStorage

语义边界:该机制实现的是“短窗口去重”,而非“全链路可审计的 jti 消费记录”。

资源访问收敛:authUuid -> 下载对象绑定(现状)

目标演进版强调“动态权限清单(scp)+ constraints + 分片级授权”。现状实现的“最小权限”更偏向以下形态:

  • 接口级最小化:关键接口均要求通过timestamp + signature (+ challengeCode)校验
  • 资源级收敛:下载资源不由自包含权限清单描述,而由authUuid查询并绑定到唯一下载对象(更像“凭证指向资源”)

现状的资源访问关键点在verificationPath(authUuid, remoteIp)

  • certifiedAuthTokenMapper.getAuthWithBrandByUuid查询认证记录
  • 校验brand/project表存在
  • 校验下载文件唯一性(必须size == 1
  • 解析下载路径softwarePath并返回下载上下文(见verificationPath
  • 写入 IP 地理信息用于审计/统计,见remoteIpAddress

现状“最小权限”的准确表述:

  • authUuid必须指向唯一下载对象,否则拒绝
  • 关键接口必须处于短期挑战上下文签名之下,否则拒绝
  • 尚未形成操作级scp的细粒度权限体系(如download_chunk/verify/report

身份到授权的映射(现状)

现状的应用层主体标识来自请求头X-VIN(代码变量名多为deviceUuid),入口集中在 controller 层(如softwareChallengeSenddownloadSoftwareStorage等)。

业务侧对该标识可信性的保障主要依赖:

  • HMAC 签名校验(签名输入包含deviceUuid
  • 挑战码上下文(后续接口签名输入包含challengeCode
  • 网关/证书体系对请求来源的约束(该部分不在业务代码中体现)

因此,现状的“身份到授权映射”可写为:

  • 第一层:提供通道安全与对端认证(TLS/mTLS)
  • 第二层:以X-VIN为应用层主体标识,结合timestamp + signature + challengeCode建立短期授权上下文
  • 资源级授权:通过authUuid查询并绑定唯一下载对象(softwarePath

层内收敛与状态依赖(现状表)

机制

实现位置

状态依赖

业务感知

mTLS 身份

网关/基础设施

主体标识(VIN/deviceUuid)

业务 Controller(X-VIN)

时间戳窗口校验

业务代码

HMAC 签名校验

业务代码(NativeSecurity/Java fallback)

挑战码签发与有效期

业务代码 + Redis Lua

Redis

反重放/短幂等

Redis NX 锁(Lua / setIfAbsent)

Redis

下载对象绑定

DB 查询(authUuid -> softwarePath)

DB

“无状态”边界可表述为:
  • 密码学验证本身(timestamp + HMAC)可以无状态
  • 授权上下文与反重放显式依赖 Redis 状态
  • 业务资格与下载对象绑定显式依赖 DB 状态

因此现状不是“完全无状态授权”,而是“密码学校验偏无状态 + 授权上下文/反重放/资源绑定有状态”的混合模式。

生产特性:弱网 / 续传 / 长下载下的授权恢复(现状)

现状针对 OTA 下载的弱网特点做了部分工程化处理:

  • 续传识别:通过Range判断isSequel,见downloadSoftwareStorage
  • 重放锁粒度:signature + rangePart(分片级去重),短 TTL2min,见downloadSoftwareStorage
  • 长下载的挑战码续期:首次下载(非续传)延长 challenge TTL(你当前为2h),见downloadSoftwareStorage

当出现“下载时间超过 challenge TTL”:

  • fetchChallengeOnly直接报CHALLENGE_EXPIRED
  • 恢复路径通常只能重新走/ota/challenge/send获取新挑战码

差异点说明:与演进方向相比,现状下载数据面仍主要由业务服务直接提供流式下载,而非对象存储直连的signed URL形态。

层间接口契约(现状版)

输出方

输出

输入方

消费方式

第一层(安全防护)

TLS/mTLS 通道(业务侧不可见)

第二层(访问控制)

默认信任网络边界/网关;业务侧依赖请求头主体标识与签名链路

第二层(访问控制)

challengeCode(Redis 有状态)+ 请求级校验结果

下载/版本业务

后续请求必须带 X-VIN/X-Timestamp/X-Signature ,并通过 challengeCode 参与的动态验签

第二层(访问控制)

反重放锁(ok:*)

第二层自身

用短 TTL NX 锁拒绝短期重复请求

第二层(访问控制)

authUuid -> 下载对象绑定

下载模块

DB 校验通过后获得唯一 softwarePath 并进入下载

现状版总结

现状第二层访问控制采用“服务端有状态挑战码 + 请求级 HMAC 校验 + Redis 短期反重放锁”的组合方案。挑战码由服务端签发并存储于 Redis,通过 TTL 控制生命周期;后续关键接口在时间戳窗口校验基础上,将challengeCode:timestamp纳入签名材料实现动态上下文签名;同时通过ok:*系列 RedisNX锁对挑战签发、版本查询与下载分片请求进行短窗口去重以降低重放风险。在资源访问方面,下载对象并非通过自包含权限清单描述,而是通过authUuid查询并绑定到唯一下载路径(softwarePath),从而实现资源级收敛与异常拒绝。该方案强调服务端控制力与工程可落地性,但与目标演进版相比,尚未形成自包含凭证(challengeToken)、操作级权限清单与数据面解耦(对象存储 signed URL)的平台化形态。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询