完成50%

This commit is contained in:
2026-07-23 08:23:59 +08:00
parent 9b0ad62df4
commit f1edc6b533
218 changed files with 24318 additions and 5514 deletions
+164
View File
@@ -0,0 +1,164 @@
(function attachJiapuTacAdapter(global) {
"use strict";
var SUPPORTED_TYPES = ["SLIDER", "ROTATE", "CONCAT", "WORD_IMAGE_CLICK"];
function fail(message, code) {
var error = new Error(message);
error.code = code || "TAC_PROTOCOL_INVALID";
throw error;
}
function text(value, label) {
if (typeof value !== "string" || !value.trim()) fail(label + "不能为空");
return value.trim();
}
function positiveInteger(value, label) {
var number = Number(value);
if (!Number.isInteger(number) || number < 1) fail(label + "必须是正整数");
return number;
}
function assertContext(context) {
if (!context || typeof context !== "object") fail("TAC 请求上下文无效");
return {
tenantId: text(context.tenantId, "租户标识"),
clientId: text(context.clientId, "客户端标识"),
sceneCode: text(context.sceneCode, "验证场景"),
subject: text(context.subject, "验证主体"),
providerCode: text(context.providerCode, "验证服务商").toUpperCase(),
captchaType: text(context.captchaType, "验证码类型").toUpperCase(),
};
}
function assertChallengePayload(type, payload) {
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
fail("行为验证挑战缺少渲染载荷");
}
if (type === "CONCAT") {
text(payload.backgroundImage, "拼图背景图片");
positiveInteger(payload.backgroundImageHeight, "拼图背景高度");
if (!payload.data || !Number.isFinite(Number(payload.data.randomY))) {
fail("拼图挑战缺少 randomY");
}
return;
}
text(payload.backgroundImage, "行为验证背景图片");
text(payload.templateImage, "行为验证模板图片");
}
function normalizeChallengeResponse(response, expectedContext) {
var context = assertContext(expectedContext);
if (!response || typeof response !== "object" || Number(response.code) !== 200) {
fail((response && response.msg) || "行为验证挑战请求失败", "TAC_CHALLENGE_FAILED");
}
var data = response.data;
if (!data || typeof data !== "object" || data.required !== true) {
fail("行为验证挑战未处于强制验证状态", "TAC_CHALLENGE_NOT_REQUIRED");
}
var providerCode = text(data.providerCode, "挑战服务商").toUpperCase();
var captchaType = text(data.captchaType, "挑战验证码类型").toUpperCase();
if (providerCode !== "TIANAI" || providerCode !== context.providerCode) {
fail("行为验证挑战服务商不匹配", "TAC_PROVIDER_MISMATCH");
}
if (SUPPORTED_TYPES.indexOf(captchaType) < 0 || captchaType !== context.captchaType) {
fail("行为验证挑战类型不匹配", "TAC_TYPE_MISMATCH");
}
var challengeId = text(data.challengeId, "行为验证挑战标识");
var expireSeconds = positiveInteger(data.expireSeconds, "行为验证挑战有效期");
assertChallengePayload(captchaType, data.payload);
return {
challenge: {
required: true,
providerCode: providerCode,
captchaType: captchaType,
challengeId: challengeId,
expireSeconds: expireSeconds,
},
sdkResponse: {
code: 200,
msg: typeof response.msg === "string" ? response.msg : "操作成功",
data: Object.assign({}, data.payload, {
type: captchaType,
id: challengeId,
}),
},
};
}
function assertTrack(track) {
if (!track || typeof track !== "object" || Array.isArray(track)) fail("行为验证轨迹无效");
positiveInteger(track.bgImageWidth, "轨迹背景宽度");
positiveInteger(track.bgImageHeight, "轨迹背景高度");
var startTime = Number(track.startTime);
var stopTime = Number(track.stopTime);
if (!Number.isFinite(startTime) || !Number.isFinite(stopTime) || stopTime < startTime) {
fail("行为验证轨迹时间无效");
}
if (!Array.isArray(track.trackList) || track.trackList.length < 1) {
fail("行为验证轨迹不能为空");
}
track.trackList.forEach(function validatePoint(point) {
if (!point || !Number.isFinite(Number(point.x)) || !Number.isFinite(Number(point.y)) ||
!Number.isFinite(Number(point.t)) || typeof point.type !== "string" || !point.type) {
fail("行为验证轨迹点无效");
}
});
return track;
}
function buildVerifyBody(libraryRequest, expectedContext, challenge) {
var context = assertContext(expectedContext);
if (!challenge || typeof challenge !== "object") fail("行为验证挑战上下文缺失");
var challengeId = text(challenge.challengeId, "行为验证挑战标识");
if (!libraryRequest || libraryRequest.id !== challengeId) {
fail("行为验证 challenge 标识与当前挑战不匹配", "TAC_CHALLENGE_MISMATCH");
}
if (challenge.providerCode !== context.providerCode || challenge.captchaType !== context.captchaType) {
fail("行为验证挑战上下文已变化", "TAC_CONTEXT_MISMATCH");
}
return {
tenantId: context.tenantId,
clientId: context.clientId,
sceneCode: context.sceneCode,
subject: context.subject,
challengeId: challengeId,
providerCode: challenge.providerCode,
captchaType: challenge.captchaType,
// 受保护 OpenAPI 的唯一合同是 payload.trackSDK 回调 id 已提升为 challengeId
// 不得再次混入 payload,也不得退回“直接传 track”的兼容分支。
payload: { track: assertTrack(libraryRequest.data) },
};
}
function normalizeVerifyResponse(response) {
if (!response || typeof response !== "object" || Number(response.code) !== 200) {
fail((response && response.msg) || "行为验证请求失败", "TAC_VERIFY_REQUEST_FAILED");
}
var data = response.data;
if (!data || data.passed !== true) {
fail((data && data.message) || "行为验证未通过", "TAC_VERIFY_REJECTED");
}
var validToken = text(data.validToken, "行为验证票据");
var expireSeconds = positiveInteger(data.expireSeconds, "行为验证票据有效期");
return {
code: 200,
data: { passed: true, validToken: validToken, expireSeconds: expireSeconds },
};
}
function toSdkFailure(error) {
return {
code: 4001,
msg: error && error.message ? error.message : "行为验证失败,请重试",
};
}
global.JiapuTacAdapter = Object.freeze({
normalizeChallengeResponse: normalizeChallengeResponse,
buildVerifyBody: buildVerifyBody,
normalizeVerifyResponse: normalizeVerifyResponse,
toSdkFailure: toSdkFailure,
});
})(typeof window !== "undefined" ? window : globalThis);