拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HarmonyOS 7 新特性实战(26):星盾风控因子导入与 JWS 结果接入

HarmonyOS 7 新特性实战(26):星盾风控因子导入与 JWS 结果接入

业务接入端侧风控,最容易犯的错误是把“设备返回了一个字符串”当成“交易可以放行”。放行前需要确认结果来自可信能力,针对当前业务请求产生,仍在有效期内,并且没有被另一次操作复用。

星盾机密风控适合单独建立沙箱实验,无需把支付场景塞进知识图鉴。一份测试订单用于说明调用边界。API 26 SDK 中可核对到 riskControlEngine 的风险因子导入、风险结果获取及 JWS 结果定义;具体策略开通与服务端验证材料仍需要对应业务接入条件。

先从服务端发起挑战

客户端选择测试订单后,服务端生成一次性挑战,把它与订单编号、用户会话、操作类型和有效时间关联。设备收到挑战后请求风险结果,再把原始结果送回服务端。客户端不自行解释几个字段后决定放行。

SDK 对 policyName 和 nonce 有长度约束,分别为 1~255 字节、16~66 字节。字节长度与字符数量不总相等。生成挑战时应使用安全随机源和约定编码,校验最终编码后的字节长度,避免中文或特殊编码导致参数错误。

下面是业务层的数据契约,不代表系统接口签名。

interfaceRiskChallenge{challengeId:string;orderId:string;action:'confirm-test-order';nonce:string;expiresAt:number;}interfaceRiskSubmission{challengeId:string;signedResult:string;}

订单内容改变以后,原挑战应失效。否则用户确认的内容与风险判断关联的内容可能不同。测试订单不连接实际资金操作,也不记录真实账户敏感数据。

JWS 解码不等于验证

JWS 可以携带签名保护的数据,但把它按分隔符拆开并解码,只能看到内容。服务端还需要按照官方信任材料和算法要求验证签名,检查挑战绑定、有效期及业务上下文。

不要信任令牌自行声明的任意验证地址或算法,也不要用“字段存在”代替真实性检查。具体证书链、密钥轮换及声明名称必须依据接入文档实现,无法脱离这些材料给出通用验签函数。

验签成功后,风险结果仍然是业务决策的输入之一。策略不可用、设备不支持或结果过期,应进入明确的人工确认、补充验证或拒绝流程;不能统一当作低风险。

因子最小化与请求生命周期

导入风险因子前,应先列明来源、用途、保存期限与必要性。SDK 提供能力不代表应用可以任意采集通讯录、位置或其他无关信息。实验只使用接入规范允许的最小测试因子。

同一个挑战只允许一个有效决策。重复点击时复用正在进行的业务任务;客户端超时后如果旧回调到达,也要检查当前 challengeId。服务端对挑战的消费采用原子操作,避免两个并发请求都通过“尚未使用”的检查。

日志记录阶段、错误码和随机请求标识即可,不输出完整签名结果、原始因子及用户身份内容。测试失败时需要能定位是能力查询、参数检查、服务调用还是服务端验证失败。

沙箱实验如何证明边界有效

正向用例使用合法挑战和已配置策略,完成设备调用、服务端验签及一次性消费。反向用例依次替换 nonce、修改测试订单、延迟到过期、再次提交同一结果以及切换到不支持能力的设备。

预期结果应写在运行前。重放用例必须被服务端拒绝;客户端按钮变灰并不能证明后端防重放有效。修改内容后签名验证失败,也不能自动证明业务挑战绑定正确,两类检查需要分别覆盖。

遇到能力不支持或策略未开通,要保存错误阶段,停止把实验结果解释为风控能力准确率。风险识别效果还需要具有合法来源和明确标签的数据集,几次手工点击无法估计误报和漏报。

沙箱闭环需要哪些材料

完整 Demo 应交付最小客户端、沙箱验证服务、挑战状态存储和脱敏测试记录。运行记录可以展示流程和错误样例,但不能暴露信任密钥或完整安全令牌。现有实现覆盖调用与信任边界,风控效果仍需要已标注的合法样本和真实策略结果。

把验证失败与业务拒绝分别返回

判定阶段失败时的含义后续路径
信任与签名结果不可接受不消费为有效风控结果
挑战及订单绑定结果不属于本次请求要求重新发起
策略结果真实结果未满足业务条件按既定沙箱策略处理

客户端判断简单,但无法独立建立服务端挑战信任;服务端验证多一段链路,却能统一一次性消费和业务决策。实施先完成挑战存储与重放用例,再配置测试策略,最后接设备结果。没有正式验证材料时,流程测试只能使用明确标注的测试信任域,不宣称已验证平台结果。

参考:华为开发者能力介绍;接口约束核对自 API 26 SDK 的 riskControlEngine 类型声明,具体策略与信任材料以业务接入文档为准。

风险因子与 JWS 结果的调用顺序

同一次检测先导入因子,再使用相同 nonce 请求已配置的策略。异步互斥避免重复点击同时发起两套流程;导入失败不进入检测,finally 解除忙碌状态。

客户端只把 JWS 当成不透明响应交给后续处理,演示界面仅显示长度。服务端需要按受信证书链与算法验证签名,再解释策略结果;不能把成功取得字符串直接当作风控通过。

exportclassRiskProbe{privatebusy:boolean=false;asyncdetect(policy:string,nonce:string):Promise<string>{InputRules.required(policy,'policy');InputRules.required(nonce,'server nonce');if(this.busy){thrownewError('detection already running');}this.busy=true;try{awaitriskControlEngine.importRiskFactors({nonce:nonce,appFactorData:[{factorName:'lab_scene',factorValue:'exhibit_preview'}]});constresponse:riskControlEngine.RiskControlDetectionResponse=awaitriskControlEngine.getRiskControlResult({policyName:policy,nonce:nonce});returnresponse.result;}finally{this.busy=false;}}}

辅助校验与边界处理使用以下实现:

exportclassInputRules{staticrequired(value:string,name:string,max:number=256):string{if(typeofvalue!=='string'||value.trim().length===0||value.length>max){thrownewError(name+' is missing or too long');}returnvalue.trim();}statichex(value:string):Uint8Array{if(!/^[0-9a-fA-F]{64}$/.test(value)){thrownewError('challenge must be 32 bytes of hex');}constbytes:Uint8Array=newUint8Array(32);for(leti:number=0;i<bytes.length;i++){bytes[i]=parseInt(value.substring(i*2,i*2+2),16);}returnbytes;}}

用失败短路和互斥检查核对源码

合法策略和因子配置、服务端 nonce、证书链信任及 JWS 验签。

检查层当前处理不能替代的结果
编译API 26 工程进行类型检查与打包目标设备支持
逻辑校验输入、状态或资源释放边界平台服务真实返回
联调按上面的输入和条件逐步执行不能以按钮文案代替核心结果

riskControlEngine 与 JWS 结果的版本约束,以 HarmonyOS 官方文档中心、当前 SDK 声明和业务接入文档为准。

风控请求的先后顺序和忙碌恢复

导入因子失败时不调用检测,同一时刻只执行一次检测,失败后解除忙碌状态。测试关心客户端编排;真实 JWS 内容仍由可信服务端校验。宿主测试直接载入 RiskProbe,仅以替身代替 riskControlEngine;load 与 test 属于测试框架。

test('risk: serial factor import and detection; unlock after failure',async()=>{constorder=[];letreject=true;constC=load('services/RiskProbe',{'@kit.DeviceSecurityKit':{riskControlEngine:{importRiskFactors:async()=>{order.push('import');if(reject)throwError('reject');},getRiskControlResult:async()=>{order.push('detect');return{result:'opaque-jws'};}}}}).RiskProbe;constx=newC();awaitassert.rejects(x.detect('p','n'));reject=false;assert.equal(awaitx.detect('p','n'),'opaque-jws');assert.deepEqual(order,['import','import','detect']);});test('risk: duplicate concurrent request rejected before a second import',async()=>{letresolve,calls=0;constpending=newPromise(r=>resolve=r);constC=load('services/RiskProbe',{'@kit.DeviceSecurityKit':{riskControlEngine:{importRiskFactors:()=>{calls++;returnpending;},getRiskControlResult:async()=>({result:'jws'})}}}).RiskProbe;constx=newC(),first=x.detect('p','n');awaitassert.rejects(x.detect('p','n'),/already/);resolve();awaitfirst;assert.equal(calls,1);});
操作或边界应检查的结果
输入不满足前提不调用后续平台操作
平台拒绝或抛错保留错误,不生成成功状态
再次进入或重试检查旧状态不会污染新任务
返回列表