mirror of
https://github.com/certd/certd.git
synced 2026-06-27 14:07:33 +08:00
chore: format
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
export type CaptchaRequest = {
|
||||
remoteIp: string,
|
||||
}
|
||||
export interface ICaptchaAddon{
|
||||
onValidate(data?:any,req?:CaptchaRequest):Promise<any>;
|
||||
getCaptcha():Promise<any>;
|
||||
remoteIp: string;
|
||||
};
|
||||
export interface ICaptchaAddon {
|
||||
onValidate(data?: any, req?: CaptchaRequest): Promise<any>;
|
||||
getCaptcha(): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { CaptchaRequest, ICaptchaAddon } from "../api.js";
|
||||
showTest: false,
|
||||
})
|
||||
export class CfTurnstileCaptcha extends BaseAddon implements ICaptchaAddon {
|
||||
|
||||
@AddonInput({
|
||||
title: "站点密钥",
|
||||
component: {
|
||||
@@ -37,30 +36,28 @@ export class CfTurnstileCaptcha extends BaseAddon implements ICaptchaAddon {
|
||||
const { remoteIp } = req;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('secret', this.secretKey);
|
||||
formData.append('response', token);
|
||||
formData.append('remoteip', remoteIp);
|
||||
formData.append("secret", this.secretKey);
|
||||
formData.append("response", token);
|
||||
formData.append("remoteip", remoteIp);
|
||||
|
||||
const res = await this.http.request({
|
||||
url: 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: formData
|
||||
})
|
||||
|
||||
if (res.success) {
|
||||
// Token is valid - process the form
|
||||
return true;
|
||||
} else {
|
||||
// Token is invalid - reject the submission
|
||||
const errorMessage = 'Cloudflare Turnstile 校验失败:' + res['error-codes'].join(', ')
|
||||
this.logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const res = await this.http.request({
|
||||
url: "https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
data: formData,
|
||||
});
|
||||
|
||||
if (res.success) {
|
||||
// Token is valid - process the form
|
||||
return true;
|
||||
} else {
|
||||
// Token is invalid - reject the submission
|
||||
const errorMessage = "Cloudflare Turnstile 校验失败:" + res["error-codes"].join(", ");
|
||||
this.logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async getCaptcha(): Promise<any> {
|
||||
|
||||
@@ -3,53 +3,50 @@ import crypto from "crypto";
|
||||
import { ICaptchaAddon } from "../api.js";
|
||||
|
||||
@IsAddon({
|
||||
addonType:"captcha",
|
||||
name: 'geetest',
|
||||
title: '极验验证码v4',
|
||||
desc: '',
|
||||
showTest:false,
|
||||
addonType: "captcha",
|
||||
name: "geetest",
|
||||
title: "极验验证码v4",
|
||||
desc: "",
|
||||
showTest: false,
|
||||
})
|
||||
export class GeeTestCaptcha extends BaseAddon implements ICaptchaAddon{
|
||||
|
||||
export class GeeTestCaptcha extends BaseAddon implements ICaptchaAddon {
|
||||
@AddonInput({
|
||||
title: '验证ID',
|
||||
title: "验证ID",
|
||||
component: {
|
||||
placeholder: 'captchaId',
|
||||
placeholder: "captchaId",
|
||||
},
|
||||
helper:"[极验验证码v4](https://console.geetest.com/sensbot/management) -> 创建业务模块 -> 新增业务场景",
|
||||
helper: "[极验验证码v4](https://console.geetest.com/sensbot/management) -> 创建业务模块 -> 新增业务场景",
|
||||
required: true,
|
||||
})
|
||||
captchaId = '';
|
||||
captchaId = "";
|
||||
|
||||
@AddonInput({
|
||||
title: '验证Key',
|
||||
title: "验证Key",
|
||||
component: {
|
||||
placeholder: 'captchaKey',
|
||||
placeholder: "captchaKey",
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
captchaKey = '';
|
||||
captchaKey = "";
|
||||
|
||||
|
||||
async onValidate(data?:any) {
|
||||
async onValidate(data?: any) {
|
||||
if (!data) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
// geetest 服务地址
|
||||
// geetest server url
|
||||
// geetest server url
|
||||
const API_SERVER = "http://gcaptcha4.geetest.com";
|
||||
|
||||
// geetest 验证接口
|
||||
// geetest server interface
|
||||
// geetest 验证接口
|
||||
// geetest server interface
|
||||
const API_URL = API_SERVER + "/validate" + "?captcha_id=" + this.captchaId;
|
||||
|
||||
|
||||
// 前端参数
|
||||
// web parameter
|
||||
var lot_number = data['lot_number'];
|
||||
var captcha_output = data['captcha_output'];
|
||||
var pass_token = data['pass_token'];
|
||||
var gen_time = data['gen_time'];
|
||||
const lot_number = data["lot_number"];
|
||||
const captcha_output = data["captcha_output"];
|
||||
const pass_token = data["pass_token"];
|
||||
const gen_time = data["gen_time"];
|
||||
if (!lot_number || !captcha_output || !pass_token || !gen_time) {
|
||||
return false;
|
||||
}
|
||||
@@ -57,23 +54,23 @@ export class GeeTestCaptcha extends BaseAddon implements ICaptchaAddon{
|
||||
// 生成签名, 使用标准的hmac算法,使用用户当前完成验证的流水号lot_number作为原始消息message,使用客户验证私钥作为key
|
||||
// 采用sha256散列算法将message和key进行单向散列生成最终的 “sign_token” 签名
|
||||
// use lot_number + CAPTCHA_KEY, generate the signature
|
||||
var sign_token = this.hmac_sha256_encode(lot_number, this.captchaKey);
|
||||
const sign_token = this.hmac_sha256_encode(lot_number, this.captchaKey);
|
||||
|
||||
// 向极验转发前端数据 + “sign_token” 签名
|
||||
// send web parameter and “sign_token” to geetest server
|
||||
var datas = {
|
||||
'lot_number': lot_number,
|
||||
'captcha_output': captcha_output,
|
||||
'pass_token': pass_token,
|
||||
'gen_time': gen_time,
|
||||
'sign_token': sign_token
|
||||
const datas = {
|
||||
lot_number: lot_number,
|
||||
captcha_output: captcha_output,
|
||||
pass_token: pass_token,
|
||||
gen_time: gen_time,
|
||||
sign_token: sign_token,
|
||||
};
|
||||
|
||||
// post request
|
||||
// 根据极验返回的用户验证状态, 网站主进行自己的业务逻辑
|
||||
// According to the user authentication status returned by the geetest, the website owner carries out his own business logic
|
||||
try{
|
||||
const res = await this.doRequest(datas, API_URL)
|
||||
try {
|
||||
const res = await this.doRequest(datas, API_URL);
|
||||
if (res.result == "success") {
|
||||
// 验证成功
|
||||
// verification successful
|
||||
@@ -81,42 +78,38 @@ export class GeeTestCaptcha extends BaseAddon implements ICaptchaAddon{
|
||||
} else {
|
||||
// 验证失败
|
||||
// verification failed
|
||||
this.logger.error("极验验证不通过 ",res.reason)
|
||||
this.logger.error("极验验证不通过 ", res.reason);
|
||||
return false;
|
||||
}
|
||||
}catch (e) {
|
||||
this.ctx.logger.error("极验验证服务异常",e)
|
||||
return true
|
||||
} catch (e) {
|
||||
this.ctx.logger.error("极验验证服务异常", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
// Generate signature
|
||||
hmac_sha256_encode(value, key){
|
||||
var hash = crypto.createHmac("sha256", key)
|
||||
.update(value, 'utf8')
|
||||
.digest('hex');
|
||||
// Generate signature
|
||||
hmac_sha256_encode(value, key) {
|
||||
const hash = crypto.createHmac("sha256", key).update(value, "utf8").digest("hex");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
// 发送post请求, 响应json数据如:{"result": "success", "reason": "", "captcha_args": {}}
|
||||
// Send a post request and respond to JSON data, such as: {result ":" success "," reason ":" "," captcha_args ": {}}
|
||||
async doRequest(datas, url){
|
||||
var options = {
|
||||
// 发送post请求, 响应json数据如:{"result": "success", "reason": "", "captcha_args": {}}
|
||||
// Send a post request and respond to JSON data, such as: {result ":" success "," reason ":" "," captcha_args ": {}}
|
||||
async doRequest(datas, url) {
|
||||
const options = {
|
||||
url: url,
|
||||
method: "POST",
|
||||
params: datas,
|
||||
timeout: 5000
|
||||
timeout: 5000,
|
||||
};
|
||||
const result = await this.ctx.http.request(options);
|
||||
return result;
|
||||
}
|
||||
|
||||
async getCaptcha(): Promise<any> {
|
||||
async getCaptcha(): Promise<any> {
|
||||
return {
|
||||
captchaId: this.captchaId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,53 +4,52 @@ import { cache } from "@certd/basic";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
@IsAddon({
|
||||
addonType:"captcha",
|
||||
name: 'image',
|
||||
title: '图片验证码',
|
||||
desc: '',
|
||||
showTest:false,
|
||||
addonType: "captcha",
|
||||
name: "image",
|
||||
title: "图片验证码",
|
||||
desc: "",
|
||||
showTest: false,
|
||||
})
|
||||
export class ImageCaptcha extends BaseAddon implements ICaptchaAddon{
|
||||
|
||||
async onValidate(data?:any) {
|
||||
export class ImageCaptcha extends BaseAddon implements ICaptchaAddon {
|
||||
async onValidate(data?: any) {
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
return await this.checkCaptcha(data.randomStr, data.imageCode)
|
||||
return await this.checkCaptcha(data.randomStr, data.imageCode);
|
||||
}
|
||||
|
||||
async getCaptchaText(randomStr:string) {
|
||||
return cache.get('imgCode:' + randomStr);
|
||||
async getCaptchaText(randomStr: string) {
|
||||
return cache.get("imgCode:" + randomStr);
|
||||
}
|
||||
|
||||
async removeCaptcha(randomStr:string) {
|
||||
cache.delete('imgCode:' + randomStr);
|
||||
async removeCaptcha(randomStr: string) {
|
||||
cache.delete("imgCode:" + randomStr);
|
||||
}
|
||||
|
||||
async checkCaptcha(randomStr: string, userCaptcha: string) {
|
||||
const code = await this.getCaptchaText(randomStr);
|
||||
if (code == null) {
|
||||
throw new Error('验证码已过期');
|
||||
throw new Error("验证码已过期");
|
||||
}
|
||||
if (code.toLowerCase() !== userCaptcha?.toLowerCase()) {
|
||||
throw new Error('验证码不正确');
|
||||
throw new Error("验证码不正确");
|
||||
}
|
||||
await this.removeCaptcha(randomStr);
|
||||
return true;
|
||||
}
|
||||
|
||||
async getCaptcha(): Promise<any> {
|
||||
const svgCaptcha = await import('svg-captcha');
|
||||
const c = svgCaptcha.create();
|
||||
//{data: '<svg.../svg>', text: 'abcd'}
|
||||
const imgCode = c.text; // = RandomUtil.randomStr(4, true);
|
||||
const randomStr = nanoid(10)
|
||||
cache.set('imgCode:' + randomStr, imgCode, {
|
||||
ttl: 2 * 60 * 1000, //过期时间 2分钟
|
||||
})
|
||||
return {
|
||||
randomStr: randomStr,
|
||||
imageData: c.data,
|
||||
}
|
||||
}
|
||||
async getCaptcha(): Promise<any> {
|
||||
const svgCaptcha = await import("svg-captcha");
|
||||
const c = svgCaptcha.create();
|
||||
//{data: '<svg.../svg>', text: 'abcd'}
|
||||
const imgCode = c.text; // = RandomUtil.randomStr(4, true);
|
||||
const randomStr = nanoid(10);
|
||||
cache.set("imgCode:" + randomStr, imgCode, {
|
||||
ttl: 2 * 60 * 1000, //过期时间 2分钟
|
||||
});
|
||||
return {
|
||||
randomStr: randomStr,
|
||||
imageData: c.data,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './geetest/index.js';
|
||||
export * from './image/index.js';
|
||||
export * from './tencent/index.js';
|
||||
export * from './cf-turnstile/index.js';
|
||||
export * from "./geetest/index.js";
|
||||
export * from "./image/index.js";
|
||||
export * from "./tencent/index.js";
|
||||
export * from "./cf-turnstile/index.js";
|
||||
|
||||
@@ -94,7 +94,7 @@ export class TencentCaptcha extends BaseAddon implements ICaptchaAddon {
|
||||
this.logger.error("腾讯云验证码账户欠费,临时放行:", err.message);
|
||||
return true;
|
||||
}
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user