👽️ 支持Gt4验证

close #162
This commit is contained in:
BTMuli
2025-11-20 00:24:08 +08:00
parent dc18cd75a7
commit 5298ecdd0a
4 changed files with 112 additions and 30 deletions

View File

@@ -1,7 +1,6 @@
/**
* @file component/func/geetest.ts
* @description 封装自定义 geetest 组件,通过函数调用的方式,简化 geetest 的使用
* @since Beta v0.7.1
* 极验验证组件封装
* @since Beta v0.8.7
*/
import type { ComponentInternalInstance, VNode } from "vue";
@@ -12,8 +11,8 @@ import geetest from "./geetest.vue";
const geetestId = "tg-func-geetest";
/**
* @description 自定义 geetest 组件
* @since Beta v0.5.1
* 自定义 geetest 组件
* @since Beta v0.8.7
* @extends ComponentInternalInstance
* @property {Function} exposeProxy.displayBox 弹出 geetest 验证
* @return GeetestInstance
@@ -22,6 +21,7 @@ interface GeetestInstance extends ComponentInternalInstance {
exposeProxy: {
displayBox: (
props: TGApp.BBS.Geetest.CreateRes,
raw?: TGApp.BBS.CaptchaLogin.CaptchaAigis,
) => Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false>;
};
}
@@ -38,21 +38,22 @@ function renderBox(props: TGApp.BBS.Geetest.CreateRes): VNode {
let geetestInstance: VNode;
/**
* @function showGeetest
* @since Beta v0.7.1
* @description 弹出 geetest 验证
* 弹出 geetest 验证
* @since Beta v0.8.7
* @param {TGApp.BBS.Geetest.CreateRes} props geetest 验证的参数
* @param {TGApp.BBS.CaptchaLogin.CaptchaAigis} raw 原始数据,一般用于 Gt4 验证
* @return {Promise<TGApp.BBS.Geetest.GeetestVerifyRes|false>} 验证成功返回验证数据
*/
async function showGeetest(
props: TGApp.BBS.Geetest.CreateRes,
raw?: TGApp.BBS.CaptchaLogin.CaptchaAigis,
): Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false> {
if (geetestInstance !== undefined) {
const boxVue = <GeetestInstance>geetestInstance.component;
return boxVue.exposeProxy.displayBox(props);
return boxVue.exposeProxy.displayBox(props, raw);
} else {
geetestInstance = renderBox(props);
return await showGeetest(props);
return await showGeetest(props, raw);
}
}

View File

@@ -16,6 +16,7 @@
</template>
<script setup lang="ts">
import "https://static.geetest.com/static/js/gt.0.4.9.js";
import "https://static.geetest.com/v4/gt4.js";
import { ref, useTemplateRef, watch } from "vue";
const show = ref<boolean>(false);
@@ -45,19 +46,51 @@ declare function initGeetest(
callback: (captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => void,
): void;
declare function initGeetest4(
params: TGApp.BBS.Geetest.InitGeetest4Params,
callback: (captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => void,
): void;
async function displayBox(
props: TGApp.BBS.Geetest.CreateRes,
raw?: TGApp.BBS.CaptchaLogin.CaptchaAigis,
): Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false> {
if ("challenge" in props) {
return await new Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false>((resolve) => {
initGeetest(
{
gt: props.gt,
challenge: props.challenge,
offline: false,
new_captcha: true,
product: "custom",
area: "#verify",
width: "250px",
},
(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => {
if (geetestEl.value === null) return;
geetestEl.value.innerHTML = "";
captchaObj.appendTo("#geetest");
captchaObj.onReady(() => (show.value = true));
captchaObj.onClose(() => {
const validate = captchaObj.getValidate();
show.value = false;
if (!validate) resolve(false);
resolve(validate);
});
},
);
});
}
return await new Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false>((resolve) => {
initGeetest(
initGeetest4(
{
gt: props.gt,
challenge: props.challenge,
offline: false,
new_captcha: true,
product: "custom",
area: "#verify",
width: "250px",
captchaId: props.gt,
riskType: props.risk_type,
product: "popup",
nextWidth: "250px",
lang: "zho",
userInfo: JSON.stringify({ session_id: raw?.session_id }),
},
(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => {
if (geetestEl.value === null) return;
@@ -65,6 +98,10 @@ async function displayBox(
captchaObj.appendTo("#geetest");
captchaObj.onReady(() => (show.value = true));
captchaObj.onClose(() => {
show.value = false;
resolve(false);
});
captchaObj.onSuccess(() => {
const validate = captchaObj.getValidate();
show.value = false;
if (!validate) resolve(false);

View File

@@ -410,7 +410,7 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
return false;
}
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
const resp = await showGeetest(JSON.parse(aigisResp.data));
const resp = await showGeetest(JSON.parse(aigisResp.data), aigisResp);
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
return await tryGetCaptcha(phone, aigisStr);
}

View File

@@ -1,7 +1,6 @@
/**
* @file types/BBS/Geetest.d.ts
* @description 米游社Geetest 类型定义文件
* @since Beta v0.8.3
* 极验验证相关类型定义文件
* @since Beta v0.8.7
*/
declare namespace TGApp.BBS.Geetest {
@@ -15,15 +14,44 @@ declare namespace TGApp.BBS.Geetest {
type CreateResp = TGApp.BBS.Response.BaseWithData<CreateRes>;
/**
* @description 极验验证的响应数据
* @since Beta v0.7.1
* @interface CreateRes
* @property {string} gt - 极验验证 gt
* @property {string} challenge - 极验验证 challenge
* @property {number} new_captcha - 极验验证 new_captcha
* @property {number} success - 极验验证 success
* 极验验证的响应数据
* @since Beta v0.8.7
*/
type CreateRes = { gt: string; challenge: string; new_captcha: number; success: number };
type CreateRes = GtCreateRes | Gt4CreateRes;
/**
* 旧版极验验证的响应数据
* @since Beta v0.8.7
*/
type GtCreateRes = {
/* gt */
gt: string;
/* challenge */
challenge: string;
/* 是否是新验证码 */
new_captcha: number;
/* 验证成功标志 */
success: number;
/* 是否使用 Gt4 验证 */
use_v4: boolean;
};
/**
* 新版 Gt4 验证的响应数据
* @since Beta v0.8.7
*/
type Gt4CreateRes = {
/* gt */
gt: string;
/* 是否是新验证码 */
new_captcha: number;
/* 风险类型 */
risk_type: string;
/* 验证成功标志 */
success: number;
/* 是否使用 Gt4 验证 */
use_v4: boolean;
};
/**
* @description 验证极验验证的响应
@@ -65,6 +93,21 @@ declare namespace TGApp.BBS.Geetest {
area: string;
};
type InitGeetest4Params = {
/* 验证ID */
captchaId: string;
/* 验证形式 */
riskType: string;
/* 展现形式 */
product: string;
/* 宽度 */
nextWidth: string;
/* 用户信息 */
userInfo: unknown;
/* 语言 */
lang: string;
};
/**
* @description Geetest 插件 captchaObj
* @since Beta v0.8.3
@@ -79,6 +122,7 @@ declare namespace TGApp.BBS.Geetest {
getValidate: () => Promise<GeetestVerifyRes>;
onClose: (callback: () => void) => boolean;
onReady: (callback: () => void) => void;
onSuccess: (callback: () => void) => void;
};
/**