接口地址: POST http://localhost/dev-api/auth/login
当前状态:
// 完整的AES+RSA双重加密登录方法
async function encryptedLogin() {
const axios = require('axios');
const CryptoJS = require('crypto-js');
const NodeRSA = require('node-rsa');
// 配置信息
const CONFIG = {
url: 'http://localhost/dev-api/auth/login',
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
tenantId: '000000',
referer: 'http://localhost/login?redirect=%252Findex',
rsaKey: 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
};
// 生成32位随机AES密钥
function generateAESKey() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 32; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// AES加密
function aesEncrypt(data, key) {
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
return CryptoJS.AES.encrypt(data, keyUtf8, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}).toString();
}
// RSA加密AES密钥
function rsaEncrypt(aesKey) {
const pemKey = `-----BEGIN PUBLIC KEY-----\n${CONFIG.rsaKey}\n-----END PUBLIC KEY-----`;
const rsaKey = new NodeRSA(pemKey);
rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
return rsaKey.encrypt(aesKeyBase64, 'base64');
}
// 生成AES密钥并加密登录数据
const aesKey = generateAESKey();
const loginData = {
tenantId: CONFIG.tenantId,
username: 'admin',
password: 'admin123',
clientId: CONFIG.clientId,
grantType: 'password',
};
const encryptedData = aesEncrypt(JSON.stringify(loginData), aesKey);
const encryptedKey = rsaEncrypt(aesKey);
// 设置请求头(按照系统要求的格式)
const headers = {
'Content-Type': 'application/json;charset=utf-8',
clientid: CONFIG.clientId, // 必需:客户端ID
'encrypt-key': encryptedKey, // 必需:RSA加密的AES密钥
referer: CONFIG.referer, // 必需:来源页面
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
isToken: 'false',
isEncrypt: 'true',
repeatSubmit: 'false',
};
// 发送登录请求
const response = await axios.post(CONFIG.url, encryptedData, {
headers: headers,
timeout: 10000,
});
// 返回Token
if (response.data.code === 200 && response.data.data?.access_token) {
return response.data.data.access_token;
} else {
throw new Error(response.data.msg || 'Login failed');
}
}
// 快速获取Token的简化版本
async function getToken(username = 'admin', password = 'admin123') {
const { getToken } = require('./test/quick-token-getter');
return await getToken(username, password);
}
# 在test目录下运行测试
cd test
npm install # 安装依赖(axios, crypto-js, node-rsa, colors)
# 完整功能版登录测试(推荐)
node custom-login-script.js
# 快速Token获取测试
node quick-token-getter.js
custom-login-script.js - 完整功能版,支持 AES+RSA 双重加密quick-token-getter.js - 快速获取版,代码简洁易懂{
"dependencies": {
"axios": "^1.7.2",
"crypto-js": "^4.2.0",
"node-rsa": "^1.1.1",
"colors": "^1.4.0"
}
}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJzeXNfdXNlcjoxIiwicm5TdHIiOiJ5MlhvbHRnd1Y2YjUzRnRMVzF4SFR1anRSUERjeGdmYyIsImNsaWVudGlkIjoiZTVjZDdlNDg5MWJmOTVkMWQxOTIwNmNlMjRhN2IzMmUiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MTAzLCJkZXB0TmFtZSI6IuaZuuiDveaOp-WItuWtpumZoiIsImRlcHRDYXRlZ29yeSI6IiIsIm5pY2tOYW1lIjoi55ax54uC55qE54uu5a2QTGkifQ.WI6iY3AgwPz7ceI8WDsVy-GaTXZnU-JKUYUTjhQdoKM
Token 解析信息:
'Content-Language': 'zh-cn' 格式'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8' 格式'Content-Language': 'zh-CN' 格式必需的 Header 格式:
{
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
'encrypt-key': '[RSA加密的AES密钥]',
'referer': 'http://localhost/login?redirect=%252Findex'
}
const config = {
baseURL: 'http://localhost/dev-api',
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=utf-8',
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
referer: 'http://localhost/login?redirect=%252Findex',
},
};
// 通用API测试方法
async function testAPI(endpoint, data, options = {}) {
const axios = require('axios');
try {
const response = await axios({
method: options.method || 'POST',
url: `http://localhost/dev-api${endpoint}`,
data: data,
headers: {
'Content-Type': 'application/json;charset=utf-8',
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
referer: 'http://localhost/login?redirect=%252Findex',
Authorization: options.token ? `Bearer ${options.token}` : undefined,
...options.headers,
},
timeout: 10000,
});
return {
success: true,
data: response.data,
status: response.status,
};
} catch (error) {
return {
success: false,
error: error.response?.data || error.message,
status: error.response?.status,
};
}
}
// 带Token的API调用示例
async function callAPIWithToken(token, endpoint, data, method = 'GET') {
return await testAPI(endpoint, data, {
method: method,
token: token,
headers: {
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
referer: 'http://localhost/login?redirect=%252Findex',
},
});
}
// 使用示例
async function getUserInfo(token) {
const result = await callAPIWithToken(token, '/system/user/getInfo', null, 'GET');
return result;
}
// 调用带参数的API
async function getUserList(token, params) {
const result = await callAPIWithToken(token, '/system/user/list', params, 'GET');
return result;
}
// 分页接口测试模板
async function testPaginationAPI(token, endpoint, params = {}) {
const defaultParams = {
pageNum: 1,
pageSize: 10,
...params,
};
const result = await callAPIWithToken(token, endpoint, defaultParams, 'GET');
if (result.success) {
console.log('分页接口测试成功:');
console.log(`总记录数: ${result.data.total}`);
console.log(`当前页数据: ${result.data.rows?.length || 0} 条`);
console.log('响应格式正确 ✅');
} else {
console.log('分页接口测试失败:', result.error);
}
return result;
}
// 使用示例
async function testUserListAPI(token) {
return await testPaginationAPI(token, '/system/user/list', {
userName: 'admin',
status: '0',
});
}
// CRUD接口完整测试套件
async function testCRUDAPIs(token, baseEndpoint, testData) {
const results = {
create: null,
read: null,
update: null,
delete: null,
};
try {
// 1. 测试创建 (CREATE)
console.log('测试创建接口...');
results.create = await callAPIWithToken(token, baseEndpoint, testData, 'POST');
if (results.create.success) {
console.log('✅ 创建接口测试通过');
const createdId = results.create.data.data?.id;
// 2. 测试读取 (READ)
if (createdId) {
console.log('测试读取接口...');
results.read = await callAPIWithToken(
token,
`${baseEndpoint}/${createdId}`,
null,
'GET',
);
if (results.read.success) {
console.log('✅ 读取接口测试通过');
// 3. 测试更新 (UPDATE)
console.log('测试更新接口...');
const updateData = {
...testData,
id: createdId,
name: testData.name + '_updated',
};
results.update = await callAPIWithToken(token, baseEndpoint, updateData, 'PUT');
if (results.update.success) {
console.log('✅ 更新接口测试通过');
}
// 4. 测试删除 (DELETE)
console.log('测试删除接口...');
results.delete = await callAPIWithToken(
token,
`${baseEndpoint}/${createdId}`,
null,
'DELETE',
);
if (results.delete.success) {
console.log('✅ 删除接口测试通过');
}
}
}
}
} catch (error) {
console.error('CRUD测试出现异常:', error);
}
return results;
}
⚠️ 本规范基于test/场地预约API测试脚本.py的开发实践制定,适用于所有新 API 模块测试
所有 API 测试脚本必须使用 AES+RSA 双重加密登录,禁止简化登录和无认证访问
# 强制依赖检查
try:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
CRYPTO_AVAILABLE = True
print("✅ 加密库已加载,将使用AES+RSA双重加密登录")
except ImportError:
print("❌ 错误:缺少必需的加密库!")
print("🔧 请安装加密库:pip install pycryptodome")
print("📖 参考文档:.cursor/cursorrules-api-test.md")
print("🚫 脚本退出,必须使用加密登录方式")
exit(1)
基于场地预约 API 测试的成功模式:
class [Module]APITest:
def __init__(self, base_url="http://localhost/dev-api"):
self.base_url = base_url
self.session = requests.Session()
self.token = None
self.user_info = None
# API测试配置 - 参考cursorrules-api-test.md
self.config = {
'clientId': 'e5cd7e4891bf95d1d19206ce24a7b32e',
'tenantId': '000000',
'referer': 'http://localhost/login?redirect=%252Findex',
'rsaKey': 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
}
# 设置标准Headers
self.session.headers.update({
'Content-Type': 'application/json;charset=utf-8',
'clientid': self.config['clientId'],
'referer': self.config['referer'],
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'isToken': 'false',
'isEncrypt': 'false',
'repeatSubmit': 'false'
})
def generate_aes_key(self):
"""生成32位随机AES密钥"""
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return ''.join(random.choices(chars, k=32))
def aes_encrypt(self, data, key):
"""AES加密 - ECB模式"""
key_bytes = key.encode('utf-8')
data_bytes = data.encode('utf-8')
padded_data = pad(data_bytes, AES.block_size)
cipher = AES.new(key_bytes, AES.MODE_ECB)
encrypted_data = cipher.encrypt(padded_data)
return base64.b64encode(encrypted_data).decode('utf-8')
def rsa_encrypt(self, aes_key):
"""RSA加密AES密钥"""
pem_key = f"-----BEGIN PUBLIC KEY-----\n{self.config['rsaKey']}\n-----END PUBLIC KEY-----"
rsa_key = RSA.import_key(pem_key)
aes_key_base64 = base64.b64encode(aes_key.encode('utf-8')).decode('utf-8')
cipher = PKCS1_v1_5.new(rsa_key)
encrypted_data = cipher.encrypt(aes_key_base64.encode('utf-8'))
return base64.b64encode(encrypted_data).decode('utf-8')
def login(self, username="admin", password="admin123"):
"""强制AES+RSA双重加密登录"""
self.log("🔐 开始AES+RSA双重加密登录...")
try:
# 生成AES密钥
aes_key = self.generate_aes_key()
# 构造登录数据
login_data = {
"tenantId": self.config['tenantId'],
"username": username,
"password": password,
"clientId": self.config['clientId'],
"grantType": "password"
}
# AES加密登录数据
login_json = json.dumps(login_data, separators=(',', ':'))
encrypted_data = self.aes_encrypt(login_json, aes_key)
# RSA加密AES密钥
encrypted_key = self.rsa_encrypt(aes_key)
# 设置加密登录Headers
headers = {
'Content-Type': 'application/json;charset=utf-8',
'clientid': self.config['clientId'],
'encrypt-key': encrypted_key,
'referer': self.config['referer'],
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'isToken': 'false',
'isEncrypt': 'true',
'repeatSubmit': 'false'
}
# 发送登录请求
response = self.session.post(
f"{self.base_url}/auth/login",
data=encrypted_data,
headers=headers,
timeout=10
)
if response.status_code == 200:
result = response.json()
if result.get('code') == 200 and result.get('data'):
# 获取Token
token_fields = ['access_token', 'token', 'accessToken']
for field in token_fields:
if field in result['data']:
self.token = result['data'][field]
break
if self.token:
self.user_info = result['data']
# 设置认证header
self.session.headers.update({
'Authorization': f'Bearer {self.token}',
'isToken': 'true',
'isEncrypt': 'false'
})
self.log("✅ AES+RSA双重加密登录成功!")
return True
self.log("❌ AES+RSA双重加密登录失败")
return False
except Exception as e:
self.log(f"❌ 登录异常: {str(e)}")
return False
def get_server_info(self):
"""检测服务可用性"""
test_endpoints = ["/actuator/health", "/auth/login"]
for endpoint in test_endpoints:
try:
response = self.session.get(f"{self.base_url}{endpoint}", timeout=5)
if response.status_code in [200, 401, 403]:
self.log(f"✓ 服务可用: {endpoint}")
return True
except:
continue
return False
def api_request(self, method, url, data=None, params=None):
"""标准API请求封装"""
try:
full_url = f"{self.base_url}{url}"
if method.upper() == 'GET':
response = self.session.get(full_url, params=params)
elif method.upper() == 'POST':
response = self.session.post(full_url, json=data, params=params)
elif method.upper() == 'PUT':
response = self.session.put(full_url, json=data)
elif method.upper() == 'DELETE':
response = self.session.delete(full_url)
self.log(f"{method} {url} - HTTP {response.status_code}")
if response.status_code == 200:
return response.json()
else:
self.log(f"请求失败: {response.text}")
return None
except Exception as e:
self.log(f"请求异常: {str(e)}")
return None
def test_entity_management(self):
"""基础实体管理测试(CRUD)"""
# 1. 查询列表
# 2. 新增数据
# 3. 查询详情
# 4. 修改数据
# 5. 删除数据
pass
def test_business_features(self):
"""业务功能测试"""
# 特定业务规则和功能测试
pass
def test_business_rules(self):
"""业务规则验证测试"""
# 边界条件和异常情况测试
pass
def cleanup_test_data(self):
"""清理测试数据"""
# 删除测试过程中创建的数据
pass
def run_all_tests(self):
"""执行完整测试流程"""
# 1. 检测服务可用性
if not self.get_server_info():
self.log("后端服务不可用,测试终止")
return False
# 2. 强制加密登录
if not self.login():
self.log("❌ AES+RSA双重加密登录失败,测试终止")
return False
try:
# 3. 执行各类测试
self.test_entity_management()
self.test_business_features()
self.test_business_rules()
finally:
# 4. 清理测试数据
self.cleanup_test_data()
return True
每个新的 API 测试脚本必须遵循:
# 必需的Python包
pip install requests pycryptodome
test/
├── [模块名]API测试脚本.py # 主测试脚本
├── [模块名]API批量测试脚本.py # 批量测试脚本(可选)
└── [模块名]API性能测试脚本.py # 性能测试脚本(可选)
基于场地预约 API 的 16 个接口模式:
基础管理接口(6 个):
业务功能接口(4 个):
辅助功能接口(6 个):
## API 测试记录 - YYYY-MM-DD
**测试环境**: http://localhost/dev-api
**测试时间**: YYYY-MM-DD HH:mm:ss
**测试人员**: [测试人员]
### 登录测试
- **方法**: AES+RSA 双重加密登录
- **结果**: ✅ 登录成功
- **Token**: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
- **用户信息**: admin / 疯狂狮子 Li / 超级管理员
- **权限**: _:_:\* (完全权限)
- **有效期**: 7 天
### 业务 API 测试
- **接口**: [接口地址]
- **方法**: [HTTP 方法]
- **权限**: [需要的权限]
- **结果**: [测试结果]
- **响应时间**: [响应时间 ms]
- **状态码**: [HTTP 状态码]
### 成功要素记录
1. **Header 配置**: 正确设置 clientid、encrypt-key、referer
2. **加密机制**: AES+RSA 双重加密正常工作
3. **语言头格式**: 使用 Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
### 验证结果
- ✅ 登录接口正常工作
- ✅ Token 有效可用于 API 调用
- ✅ 用户权限验证通过
- ✅ 加密解密机制正常
# API 接口测试报告
## 🎯 测试概览
**测试时间**: YYYY-MM-DD HH:mm:ss
**测试范围**: [具体测试的 API 模块]
**测试环境**: http://localhost/dev-api
**测试工具**: Node.js + Axios + 自定义脚本
## 📁 测试脚本路径
**登录脚本**: `test/custom-login-script.js`
**API 测试脚本**: `test/[功能]-api-test.js`
## 🔐 认证测试
### 登录接口测试
- **接口地址**: POST /auth/login
- **加密方式**: AES+RSA 双重加密
- **测试结果**: ✅ 成功
- **Token 获取**: ✅ 正常
- **Token 有效期**: 7 天
### Token 验证测试
- **Authorization 格式**: Bearer {token}
- **必需 Header**: clientid, referer, Accept-Language
- **权限验证**: ✅ admin 用户完全权限
## 🔍 业务接口测试
### [模块名称]接口测试
#### 列表查询接口
- **接口**: GET /[module]/list
- **参数**: pageNum, pageSize, [查询条件]
- **响应格式**: {total, rows, code, msg}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 详情查询接口
- **接口**: GET /[module]/{id}
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 新增接口
- **接口**: POST /[module]
- **请求格式**: JSON 对象
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 修改接口
- **接口**: PUT /[module]
- **请求格式**: JSON 对象(包含 id)
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
#### 删除接口
- **接口**: DELETE /[module]/{id}
- **响应格式**: {code, msg, data}
- **测试结果**: ✅ 通过
- **响应时间**: [x]ms
## 📊 测试统计
- **总接口数**: [数量]
- **测试通过**: [数量]
- **测试失败**: [数量]
- **通过率**: [百分比]%
## ⚠️ 问题记录
[记录测试中发现的问题]
## 🎯 结论
[测试结论和建议]
---
**测试执行**: 基于 `test/` 目录下的测试脚本
**文档路径**: `docs/测试文档/[模块]/[功能]/[报告名称]-YYYY-MM-DD.md`
test/custom-login-script.js 获取 TokenAccept-Language: zh-CN,zh;q=0.9,en;q=0.8test/custom-login-script.js 和 test/quick-token-getter.jstest/登录脚本使用说明.mddocs/开发文档/API.mddocs/变更管理/code-adjust-record.mdquick-token-getter.js 快速获取 Token 进行测试custom-login-script.js 进行完整的登录流程测试// 完整的API自动化测试脚本示例
async function runCompleteAPITest() {
console.log('🚀 开始API自动化测试...\n');
try {
// 1. 获取Token
console.log('1️⃣ 获取登录Token...');
const token = await encryptedLogin();
console.log('✅ Token获取成功\n');
// 2. 测试用户信息接口
console.log('2️⃣ 测试用户信息接口...');
const userInfo = await getUserInfo(token);
if (userInfo.success) {
console.log('✅ 用户信息接口测试通过');
console.log(`用户: ${userInfo.data.data.user.userName}`);
console.log(`权限: ${userInfo.data.data.permissions}\n`);
}
// 3. 测试分页列表接口
console.log('3️⃣ 测试分页列表接口...');
const userList = await testUserListAPI(token);
if (userList.success) {
console.log('✅ 分页列表接口测试通过\n');
}
// 4. 测试CRUD接口(可选)
console.log('4️⃣ 测试CRUD接口...');
// const crudResults = await testCRUDAPIs(token, '/system/dept', testDeptData);
console.log('🎉 API自动化测试完成!');
} catch (error) {
console.error('❌ 测试失败:', error.message);
}
}
// 执行测试
runCompleteAPITest();
// API性能测试脚本
async function performanceTest(token, endpoint, concurrent = 10, iterations = 100) {
console.log(`开始性能测试: ${endpoint}`);
console.log(`并发数: ${concurrent}, 迭代次数: ${iterations}\n`);
const startTime = Date.now();
const results = [];
for (let i = 0; i < concurrent; i++) {
const promises = [];
for (let j = 0; j < iterations / concurrent; j++) {
promises.push(
callAPIWithToken(token, endpoint, null, 'GET')
.then((result) => ({
success: result.success,
time: Date.now(),
}))
.catch((error) => ({
success: false,
error: error.message,
time: Date.now(),
})),
);
}
results.push(...(await Promise.all(promises)));
}
const endTime = Date.now();
const totalTime = endTime - startTime;
const successCount = results.filter((r) => r.success).length;
const failCount = results.length - successCount;
console.log('📊 性能测试结果:');
console.log(`总请求数: ${results.length}`);
console.log(`成功数: ${successCount}`);
console.log(`失败数: ${failCount}`);
console.log(`成功率: ${((successCount / results.length) * 100).toFixed(2)}%`);
console.log(`总耗时: ${totalTime}ms`);
console.log(`平均响应时间: ${(totalTime / results.length).toFixed(2)}ms`);
console.log(`QPS: ${(results.length / (totalTime / 1000)).toFixed(2)} req/s`);
return {
total: results.length,
success: successCount,
fail: failCount,
successRate: successCount / results.length,
totalTime,
avgResponseTime: totalTime / results.length,
qps: results.length / (totalTime / 1000),
};
}
🎉 成功状态: 登录获取 Token 功能已完全实现并正常工作,可以稳定用于 API 开发和测试。