Files
sass-admin-agent-api/src/security.ts
2025-11-27 18:22:26 +08:00

83 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// src/security.ts
import { Elysia, Context } from 'elysia';
// 脱敏规则配置
const MASK_RULES: Record<string, (value: string) => string> = {
// 手机号138****1234
mobile: (v) => v.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
phone: (v) => v.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
// 邮箱ab***@example.com
email: (v) => v.replace(/(.{2}).+(@.*)/, '$1***$2'),
// 身份证110***********1234
id_card: (v) => v.length > 8
? v.substring(0, 3) + '*'.repeat(v.length - 7) + v.substring(v.length - 4)
: v,
// 银行卡6222 **** **** 1234
bank_card: (v) => v.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** $2'),
// 用户名:如果长度>4则中间用*
nickname: (v) => v.length > 4
? v.substring(0, 2) + '*'.repeat(v.length - 4) + v.substring(v.length - 2)
: v,
// 默认规则
default: (v) => v.length > 6
? v.substring(0, 2) + '*'.repeat(v.length - 4) + v.substring(v.length - 2)
: v
};
// 判断是否为敏感字段(支持模糊匹配)
const isSensitiveField = (key: string): boolean => {
const sensitiveKeys = ['mobile', 'phone', 'email', 'id_card', 'bank_card', 'nickname', 'realname', 'address'];
return sensitiveKeys.some(term => key.toLowerCase().includes(term));
};
// 获取脱敏函数
const getMaskFn = (key: string): ((v: string) => string) => {
for (const [term, fn] of Object.entries(MASK_RULES)) {
if (key.toLowerCase().includes(term)) {
return fn;
}
}
return MASK_RULES.default || ((v: string) => v);
};
// 递归脱敏
export const sanitizeResult = (data: any): any => {
if (Array.isArray(data)) {
return data.map(sanitizeResult);
}
if (typeof data === 'object' && data !== null) {
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(data)) {
if (value == null) {
result[key] = value;
} else if (isSensitiveField(key)) {
result[key] = typeof value === 'string' ? getMaskFn(key)(value) : value;
} else if (typeof value === 'object') {
result[key] = sanitizeResult(value);
} else {
result[key] = value;
}
}
return result;
}
return data;
};
// API Key 验证(修复:使用 Elysia 插件类型)
export const verifyApiKey = (app: Elysia) => app.onBeforeHandle((ctx) => {
const authHeader = ctx.request.headers.get('authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.substring(7);
if (token !== process.env.ADMIN_API_KEY) {
return new Response('Forbidden', { status: 403 });
}
});