Feat: add bili captcha
This commit is contained in:
parent
f3d08649a3
commit
d52d94b293
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"bili",
|
"bili",
|
||||||
|
"Geetest",
|
||||||
"popconfirm"
|
"popconfirm"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
<link
|
<link
|
||||||
href="https://font.sec.miui.com/font/css?family=MiSans:400,500,600,700:Chinese_Simplify,Latin,Chinese_Traditional&display=swap"
|
href="https://font.sec.miui.com/font/css?family=MiSans:400,500,600,700:Chinese_Simplify,Latin,Chinese_Traditional&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
|
<script src="/public/js/gt.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -0,0 +1,353 @@
|
||||||
|
"v0.4.8 Geetest Inc.";
|
||||||
|
|
||||||
|
(function (window) {
|
||||||
|
"use strict";
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
throw new Error('Geetest requires browser environment');
|
||||||
|
}
|
||||||
|
|
||||||
|
var document = window.document;
|
||||||
|
var Math = window.Math;
|
||||||
|
var head = document.getElementsByTagName("head")[0];
|
||||||
|
|
||||||
|
function _Object(obj) {
|
||||||
|
this._obj = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Object.prototype = {
|
||||||
|
_each: function (process) {
|
||||||
|
var _obj = this._obj;
|
||||||
|
for (var k in _obj) {
|
||||||
|
if (_obj.hasOwnProperty(k)) {
|
||||||
|
process(k, _obj[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Config(config) {
|
||||||
|
var self = this;
|
||||||
|
new _Object(config)._each(function (key, value) {
|
||||||
|
self[key] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.prototype = {
|
||||||
|
api_server: 'api.geetest.com',
|
||||||
|
protocol: 'http://',
|
||||||
|
typePath: '/gettype.php',
|
||||||
|
fallback_config: {
|
||||||
|
slide: {
|
||||||
|
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
|
||||||
|
type: 'slide',
|
||||||
|
slide: '/static/js/geetest.0.0.0.js'
|
||||||
|
},
|
||||||
|
fullpage: {
|
||||||
|
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
|
||||||
|
type: 'fullpage',
|
||||||
|
fullpage: '/static/js/fullpage.0.0.0.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_get_fallback_config: function () {
|
||||||
|
var self = this;
|
||||||
|
if (isString(self.type)) {
|
||||||
|
return self.fallback_config[self.type];
|
||||||
|
} else if (self.new_captcha) {
|
||||||
|
return self.fallback_config.fullpage;
|
||||||
|
} else {
|
||||||
|
return self.fallback_config.slide;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_extend: function (obj) {
|
||||||
|
var self = this;
|
||||||
|
new _Object(obj)._each(function (key, value) {
|
||||||
|
self[key] = value;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var isNumber = function (value) {
|
||||||
|
return (typeof value === 'number');
|
||||||
|
};
|
||||||
|
var isString = function (value) {
|
||||||
|
return (typeof value === 'string');
|
||||||
|
};
|
||||||
|
var isBoolean = function (value) {
|
||||||
|
return (typeof value === 'boolean');
|
||||||
|
};
|
||||||
|
var isObject = function (value) {
|
||||||
|
return (typeof value === 'object' && value !== null);
|
||||||
|
};
|
||||||
|
var isFunction = function (value) {
|
||||||
|
return (typeof value === 'function');
|
||||||
|
};
|
||||||
|
var MOBILE = /Mobi/i.test(navigator.userAgent);
|
||||||
|
var pt = MOBILE ? 3 : 0;
|
||||||
|
|
||||||
|
var callbacks = {};
|
||||||
|
var status = {};
|
||||||
|
|
||||||
|
var nowDate = function () {
|
||||||
|
var date = new Date();
|
||||||
|
var year = date.getFullYear();
|
||||||
|
var month = date.getMonth() + 1;
|
||||||
|
var day = date.getDate();
|
||||||
|
var hours = date.getHours();
|
||||||
|
var minutes = date.getMinutes();
|
||||||
|
var seconds = date.getSeconds();
|
||||||
|
|
||||||
|
if (month >= 1 && month <= 9) {
|
||||||
|
month = '0' + month;
|
||||||
|
}
|
||||||
|
if (day >= 0 && day <= 9) {
|
||||||
|
day = '0' + day;
|
||||||
|
}
|
||||||
|
if (hours >= 0 && hours <= 9) {
|
||||||
|
hours = '0' + hours;
|
||||||
|
}
|
||||||
|
if (minutes >= 0 && minutes <= 9) {
|
||||||
|
minutes = '0' + minutes;
|
||||||
|
}
|
||||||
|
if (seconds >= 0 && seconds <= 9) {
|
||||||
|
seconds = '0' + seconds;
|
||||||
|
}
|
||||||
|
var currentdate = year + '-' + month + '-' + day + " " + hours + ":" + minutes + ":" + seconds;
|
||||||
|
return currentdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
var random = function () {
|
||||||
|
return parseInt(Math.random() * 10000) + (new Date()).valueOf();
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadScript = function (url, cb) {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.charset = "UTF-8";
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
// 对geetest的静态资源添加 crossOrigin
|
||||||
|
if (/static\.geetest\.com/g.test(url)) {
|
||||||
|
script.crossOrigin = "anonymous";
|
||||||
|
}
|
||||||
|
|
||||||
|
script.onerror = function () {
|
||||||
|
cb(true);
|
||||||
|
};
|
||||||
|
var loaded = false;
|
||||||
|
script.onload = script.onreadystatechange = function () {
|
||||||
|
if (!loaded &&
|
||||||
|
(!script.readyState ||
|
||||||
|
"loaded" === script.readyState ||
|
||||||
|
"complete" === script.readyState)) {
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
cb(false);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
script.src = url;
|
||||||
|
head.appendChild(script);
|
||||||
|
};
|
||||||
|
|
||||||
|
var normalizeDomain = function (domain) {
|
||||||
|
// special domain: uems.sysu.edu.cn/jwxt/geetest/
|
||||||
|
// return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
|
||||||
|
return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
|
||||||
|
};
|
||||||
|
var normalizePath = function (path) {
|
||||||
|
path = path.replace(/\/+/g, '/');
|
||||||
|
if (path.indexOf('/') !== 0) {
|
||||||
|
path = '/' + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
var normalizeQuery = function (query) {
|
||||||
|
if (!query) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var q = '?';
|
||||||
|
new _Object(query)._each(function (key, value) {
|
||||||
|
if (isString(value) || isNumber(value) || isBoolean(value)) {
|
||||||
|
q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (q === '?') {
|
||||||
|
q = '';
|
||||||
|
}
|
||||||
|
return q.replace(/&$/, '');
|
||||||
|
};
|
||||||
|
var makeURL = function (protocol, domain, path, query) {
|
||||||
|
domain = normalizeDomain(domain);
|
||||||
|
|
||||||
|
var url = normalizePath(path) + normalizeQuery(query);
|
||||||
|
if (domain) {
|
||||||
|
url = protocol + domain + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
var load = function (config, send, protocol, domains, path, query, cb) {
|
||||||
|
var tryRequest = function (at) {
|
||||||
|
|
||||||
|
var url = makeURL(protocol, domains[at], path, query);
|
||||||
|
loadScript(url, function (err) {
|
||||||
|
if (err) {
|
||||||
|
if (at >= domains.length - 1) {
|
||||||
|
cb(true);
|
||||||
|
// report gettype error
|
||||||
|
if (send) {
|
||||||
|
config.error_code = 508;
|
||||||
|
var url = protocol + domains[at] + path;
|
||||||
|
reportError(config, url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tryRequest(at + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
tryRequest(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var jsonp = function (domains, path, config, callback) {
|
||||||
|
if (isObject(config.getLib)) {
|
||||||
|
config._extend(config.getLib);
|
||||||
|
callback(config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (config.offline) {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cb = "geetest_" + random();
|
||||||
|
window[cb] = function (data) {
|
||||||
|
if (data.status == 'success') {
|
||||||
|
callback(data.data);
|
||||||
|
} else if (!data.status) {
|
||||||
|
callback(data);
|
||||||
|
} else {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
}
|
||||||
|
window[cb] = undefined;
|
||||||
|
try {
|
||||||
|
delete window[cb];
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
load(config, true, config.protocol, domains, path, {
|
||||||
|
gt: config.gt,
|
||||||
|
callback: cb
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var reportError = function (config, url) {
|
||||||
|
load(config, false, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
|
||||||
|
time: nowDate(),
|
||||||
|
captcha_id: config.gt,
|
||||||
|
challenge: config.challenge,
|
||||||
|
pt: pt,
|
||||||
|
exception_url: url,
|
||||||
|
error_code: config.error_code
|
||||||
|
}, function (err) { })
|
||||||
|
}
|
||||||
|
|
||||||
|
var throwError = function (errorType, config) {
|
||||||
|
var errors = {
|
||||||
|
networkError: '网络错误',
|
||||||
|
gtTypeError: 'gt字段不是字符串类型'
|
||||||
|
};
|
||||||
|
if (typeof config.onError === 'function') {
|
||||||
|
config.onError(errors[errorType]);
|
||||||
|
} else {
|
||||||
|
throw new Error(errors[errorType]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var detect = function () {
|
||||||
|
return window.Geetest || document.getElementById("gt_lib");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (detect()) {
|
||||||
|
status.slide = "loaded";
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initGeetest = function (userConfig, callback) {
|
||||||
|
|
||||||
|
var config = new Config(userConfig);
|
||||||
|
|
||||||
|
if (userConfig.https) {
|
||||||
|
config.protocol = 'https://';
|
||||||
|
} else if (!userConfig.protocol) {
|
||||||
|
config.protocol = window.location.protocol + '//';
|
||||||
|
}
|
||||||
|
|
||||||
|
// for KFC
|
||||||
|
if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
|
||||||
|
userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
|
||||||
|
config.apiserver = 'yumchina.geetest.com/'; // for old js
|
||||||
|
config.api_server = 'yumchina.geetest.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userConfig.gt) {
|
||||||
|
window.GeeGT = userConfig.gt
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userConfig.challenge) {
|
||||||
|
window.GeeChallenge = userConfig.challenge
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObject(userConfig.getType)) {
|
||||||
|
config._extend(userConfig.getType);
|
||||||
|
}
|
||||||
|
jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
|
||||||
|
var type = newConfig.type;
|
||||||
|
var init = function () {
|
||||||
|
config._extend(newConfig);
|
||||||
|
callback(new window.Geetest(config));
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks[type] = callbacks[type] || [];
|
||||||
|
var s = status[type] || 'init';
|
||||||
|
if (s === 'init') {
|
||||||
|
status[type] = 'loading';
|
||||||
|
|
||||||
|
callbacks[type].push(init);
|
||||||
|
|
||||||
|
load(config, true, config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
|
||||||
|
if (err) {
|
||||||
|
status[type] = 'fail';
|
||||||
|
throwError('networkError', config);
|
||||||
|
} else {
|
||||||
|
status[type] = 'loaded';
|
||||||
|
var cbs = callbacks[type];
|
||||||
|
for (var i = 0, len = cbs.length; i < len; i = i + 1) {
|
||||||
|
var cb = cbs[i];
|
||||||
|
if (isFunction(cb)) {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbacks[type] = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (s === "loaded") {
|
||||||
|
init();
|
||||||
|
} else if (s === "fail") {
|
||||||
|
throwError('networkError', config);
|
||||||
|
} else if (s === "loading") {
|
||||||
|
callbacks[type].push(init);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
})(window);
|
|
@ -17,7 +17,7 @@ body {
|
||||||
.l-input {
|
.l-input {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
border: 2px solid #4b4b4b;
|
border: 2px solid #696969;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
@apply dark:bg-zinc-900;
|
@apply dark:bg-zinc-900;
|
||||||
|
|
|
@ -21,3 +21,18 @@ export const veriBiliBiliQRCode = useDefineApi<
|
||||||
url: "/api/vendor/bilibili/login/qr",
|
url: "/api/vendor/bilibili/login/qr",
|
||||||
method: "POST"
|
method: "POST"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取 哔哩哔哩 人机验证
|
||||||
|
export const getBiliBiliCaptcha = useDefineApi<
|
||||||
|
{
|
||||||
|
headers: { Authorization: string };
|
||||||
|
},
|
||||||
|
{
|
||||||
|
token: string;
|
||||||
|
gt: string;
|
||||||
|
challenge: string;
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
url: "/api/vendor/bilibili/login/captcha",
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { getBiliBiliQRCode, veriBiliBiliQRCode } from "@/services/apis/vendor";
|
import { getBiliBiliQRCode, veriBiliBiliQRCode, getBiliBiliCaptcha } from "@/services/apis/vendor";
|
||||||
import QRCodeVue3 from "qrcode-vue3";
|
import QRCodeVue3 from "qrcode-vue3";
|
||||||
import { roomStore } from "@/stores/room";
|
import { roomStore } from "@/stores/room";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
@ -30,6 +30,7 @@ const useBilibiliLogin = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 验证二维码
|
||||||
const { state: biliQRCodeStatus, execute: reqVeriBiliBiliQRCode } = veriBiliBiliQRCode();
|
const { state: biliQRCodeStatus, execute: reqVeriBiliBiliQRCode } = veriBiliBiliQRCode();
|
||||||
const vBiliQRCode = () => {
|
const vBiliQRCode = () => {
|
||||||
if (getQRCodeStatus) clearInterval(getQRCodeStatus);
|
if (getQRCodeStatus) clearInterval(getQRCodeStatus);
|
||||||
|
@ -61,6 +62,54 @@ const vBiliQRCode = () => {
|
||||||
}, 2000);
|
}, 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取人机验证
|
||||||
|
const { state: biliCaptcha, execute: reqBiliBiliCaptcha } = getBiliBiliCaptcha();
|
||||||
|
const getBiliCaptcha = async () => {
|
||||||
|
try {
|
||||||
|
await reqBiliBiliCaptcha({
|
||||||
|
headers: {
|
||||||
|
Authorization: userToken
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (biliCaptcha.value) {
|
||||||
|
// 调用 initGeetest 进行初始化
|
||||||
|
// 参数1:配置参数
|
||||||
|
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它调用相应的接口
|
||||||
|
(window as any).initGeetest(
|
||||||
|
{
|
||||||
|
// 以下 4 个配置参数为必须,不能缺少
|
||||||
|
gt: biliCaptcha.value.gt,
|
||||||
|
challenge: biliCaptcha.value.challenge,
|
||||||
|
offline: false, // 表示用户后台检测极验服务器是否宕机
|
||||||
|
new_captcha: true, // 用于宕机时表示是新验证码的宕机
|
||||||
|
|
||||||
|
product: "popup", // 产品形式,包括:float,popup
|
||||||
|
width: "300px",
|
||||||
|
https: true
|
||||||
|
|
||||||
|
// 更多前端配置参数说明请参见:http://docs.geetest.com/install/client/web-front/
|
||||||
|
},
|
||||||
|
function captchaHandler(captchaObj: any) {
|
||||||
|
(window as any).captchaObj = captchaObj;
|
||||||
|
captchaObj.appendTo("#captcha");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function validate() {
|
||||||
|
var result = (window as any).captchaObj.getValidate();
|
||||||
|
if (!result) {
|
||||||
|
alert("请先完成验证!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alert("OK");
|
||||||
|
// do sth
|
||||||
|
}
|
||||||
|
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
bili_login_dialog.value = false;
|
bili_login_dialog.value = false;
|
||||||
clearInterval(getQRCodeStatus);
|
clearInterval(getQRCodeStatus);
|
||||||
|
@ -133,10 +182,12 @@ const closeDialog = () => {
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :md="14">
|
<el-col :md="14">
|
||||||
<h2 class="text-xl font-semibold">短信登录</h2>
|
<h2 class="text-xl font-semibold">短信登录</h2>
|
||||||
<input type="text" class="l-input" placeholder="手机号" />
|
<input type="number" class="l-input" placeholder="手机号" />
|
||||||
<input type="text" class="l-input" placeholder="短信验证码" />
|
<input type="number" class="l-input" placeholder="短信验证码" />
|
||||||
<button class="btn">发送验证码</button>
|
<div id="captcha"></div>
|
||||||
<button class="btn btn-success">登录</button>
|
<button class="btn" @click="getBiliCaptcha">发送验证码</button>
|
||||||
|
|
||||||
|
<button class="btn btn-success" @click="validate">登录</button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue