🔐 签名接口
ThinkAdmin 提供完善的签名接口机制,支持安全的 API 接口调用和数据验证。
🚀 主要功能
- 安全签名: 基于 MD5 的接口数据签名机制
- 时间验证: 支持时间戳验证,防止重放攻击
- 随机字符串: 使用随机字符串增强安全性
- 调试模式: 支持调试模式,方便开发测试
- 分页支持: 支持列表分页功能
- 文件传输: 支持 base64 文件传输
📋 请求字段说明
必需字段
- appid: 接口请求认证账号,需要保持两端一致
- time: 接口请求时间(时差不能超过30秒),使用时间戳,单位为秒
- nostr: 接口请求随机字符串,可以使用随机函数生成
- data: 接口请求数据(数据需转为JSON格式),需以原格式传输
- sign: 接口数据签名,按特定规则生成
签名规则
- 字段顺序: 依次为
appid、data、time、appkey、nostr - 连接方式: 五个字段以
#连接 - 加密方式: 进行 MD5 编码获取签名
- 私钥保护:
appkey为接口私钥,仅用于签名且不参与接口数据请求
⚙️ 特殊说明
文件传输
- 传输方式: 使用 base64 传输文件
- 数据格式: 需要去掉数据头部
- 大文件处理: 建议直接对接云存储
- 性能考虑: 大文件传输可能影响性能
调试模式
- 调试开关: 当接口的
debug为true时 - 简化请求: 不需要传签名内容
- 普通表单: 只需要以普通表单传原接口的
data内容 - 格式要求: 非JSON格式,方便调试编写文档
- 开发便利: 简化开发阶段的接口调试
列表分页
- 分页参数: 使用
get方式传page参数 - 签名排除:
page不需要参与签名 - 数据格式: 传在
data的 JSON 中会无效 - 参数不符: 因为数据格式与 Page 参数不符
- 性能优化: 分页参数独立处理提升性能
安全机制
- 时间验证: 时间戳验证防止重放攻击
- 随机字符串: 使用随机字符串增强安全性
- 签名验证: 严格的签名验证机制
- 私钥保护: 私钥不参与接口数据请求
🔧 实际应用案例
前端客户端请求案例
基础请求示例
// 1. 加载 MD5 插件
require(['md5'], function(md5) {
// 2. 配置接口参数
const config = {
appid: 'your_app_id', // 接口账号
appkey: 'your_app_key', // 接口密钥
apiUrl: 'https://your-domain.com/api/data/getUserInfo' // 接口地址
};
// 3. 准备请求数据
const requestData = {
user_id: 123,
page: 1,
limit: 10
};
// 4. 生成签名
function generateSign(appid, data, appkey) {
const nostr = Math.random().toString(36).substr(2, 15);
const time = Math.ceil(Date.now() / 1000);
const json = JSON.stringify(data);
const signString = [appid, json, time, appkey, nostr].join('#');
const sign = md5(signString);
return {
appid: appid,
time: time,
nostr: nostr,
data: json,
sign: sign
};
}
// 5. 发起请求
const signData = generateSign(config.appid, requestData, config.appkey);
$.ajax({
url: config.apiUrl,
type: 'POST',
data: signData,
dataType: 'json',
success: function(response) {
if (response.code === 1) {
console.log('请求成功:', response.data);
// 处理成功响应
} else {
console.error('请求失败:', response.info);
// 处理错误响应
}
},
error: function(xhr, status, error) {
console.error('请求异常:', error);
}
});
});封装成通用方法
// 封装签名接口请求方法
class ApiClient {
constructor(appid, appkey, baseUrl) {
this.appid = appid;
this.appkey = appkey;
this.baseUrl = baseUrl;
}
// 生成签名
generateSign(data) {
const nostr = Math.random().toString(36).substr(2, 15);
const time = Math.ceil(Date.now() / 1000);
const json = JSON.stringify(data);
const signString = [this.appid, json, time, this.appkey, nostr].join('#');
const sign = md5(signString);
return {
appid: this.appid,
time: time,
nostr: nostr,
data: json,
sign: sign
};
}
// 发起请求
request(endpoint, data = {}) {
return new Promise((resolve, reject) => {
const signData = this.generateSign(data);
$.ajax({
url: this.baseUrl + endpoint,
type: 'POST',
data: signData,
dataType: 'json',
success: function(response) {
if (response.code === 1) {
resolve(response.data);
} else {
reject(new Error(response.info));
}
},
error: function(xhr, status, error) {
reject(new Error(error));
}
});
});
}
}
// 使用示例
const api = new ApiClient('your_app_id', 'your_app_key', 'https://your-domain.com/api/');
// 获取用户列表
api.request('/data/getUserList', { page: 1, limit: 10 })
.then(data => {
console.log('用户列表:', data);
})
.catch(error => {
console.error('请求失败:', error.message);
});
// 获取用户详情
api.request('/data/getUserInfo', { user_id: 123 })
.then(data => {
console.log('用户详情:', data);
})
.catch(error => {
console.error('请求失败:', error.message);
});调试模式示例
// 调试模式下的简化请求
function debugRequest(endpoint, data) {
// 调试模式下直接发送数据,不需要签名
$.ajax({
url: 'https://your-domain.com/api' + endpoint,
type: 'POST',
data: data, // 直接发送原始数据
dataType: 'json',
success: function(response) {
console.log('调试请求成功:', response);
},
error: function(xhr, status, error) {
console.error('调试请求失败:', error);
}
});
}
// 使用调试模式
debugRequest('/data/getUserList', {
page: 1,
limit: 10,
debug: true // 开启调试模式
});后端客户端请求案例:
<?php
declare(strict_types=1);
use think\admin\service\InterfaceService;
// 实例接口服务对象
$service = InterfaceService::instance();
// 设置接口请求网关(可选)
$service->getway('https://api.example.com');
// 设置接口认证账号
$service->setAuth('your_app_id', 'your_app_key');
// 准备请求数据
$data = [
'user_id' => 123,
'action' => 'getUserInfo'
];
// 发起接口请求
$result = $service->doRequest('/api/data/getUserInfo', $data);
// 处理返回结果
if ($result['code'] === 1) {
echo "请求成功:" . $result['info'] . "\n";
print_r($result['data']);
} else {
echo "请求失败:" . $result['info'] . "\n";
}服务端接口控制器案例:
<?php
declare(strict_types=1);
namespace app\data\controller\api;
use think\admin\Controller;
use think\admin\service\InterfaceService;
/**
* 接口授权基础控制器
* @class Auth
* @package app\data\controller\api
*/
class Auth extends Controller
{
/**
* 当前请求数据
* @var array
*/
protected $data;
/**
* 接口ID
* @var string
*/
protected $appid = 'your_app_id';
/**
* 接口密钥
* @var string
*/
protected $appkey = 'your_app_key';
/**
* 接口服务对象
* @var InterfaceService
*/
protected $interface;
/**
* 接口授权初始化
*/
protected function initialize()
{
// 实例接口对象
$this->interface = InterfaceService::instance();
// 设置接口认证账号
$this->interface->setAuth($this->appid, $this->appkey);
// 获取并验证接口数据(会自动验证签名)
$this->data = $this->interface->getData();
}
/**
* 响应错误消息
* @param mixed $info 错误信息
* @param mixed $data 错误数据
* @param mixed $code 错误代码
*/
public function error($info, $data = '{-null-}', $code = 0): void
{
$this->interface->error($info, $data, $code);
}
/**
* 响应成功消息
* @param mixed $info 成功信息
* @param mixed $data 成功数据
* @param mixed $code 成功代码
*/
public function success($info, $data = '{-null-}', $code = 1): void
{
$this->interface->success($info, $data, $code);
}
/**
* 响应高级数据
* @param array $data 响应数据
*/
protected function response(array $data = []): void
{
$this->interface->baseSuccess('接口调用成功!', $data);
}
}如果需要使用数据库来管理接口账号,需要调整下初始化方法:
<?php
declare(strict_types=1);
namespace app\data\controller\api;
use think\admin\Controller;
use think\admin\service\InterfaceService;
/**
* 接口授权基础控制器(数据库管理账号版本)
* @class Auth
* @package app\data\controller\api
*/
class Auth extends Controller
{
/**
* 当前请求数据
* @var array
*/
protected $data;
/**
* 接口账号信息
* @var array
*/
protected $user;
/**
* 接口服务对象
* @var InterfaceService
*/
protected $interface;
/**
* 接口授权初始化(从数据库读取账号)
*/
protected function initialize()
{
// 接口实例初始化
$this->interface = InterfaceService::instance();
// 验证 appid 参数
$map = $this->_vali(['appid.require' => '参数APPID不能为空!']);
// 从数据库中查询账号(使用自己的数据表)
$this->user = $this->app->db->name('AppUser')->where($map)->find();
// 验证账号是否存在
if (empty($this->user)) {
$this->interface->error('接口账号不存在!');
}
// 验证账号状态
if (empty($this->user['status'])) {
$this->interface->error('接口账号已被禁用!');
}
// 验证账号是否已删除
if (!empty($this->user['is_deleted'])) {
$this->interface->error('接口账号已被移除!');
}
// 接口数据初始化(使用数据库中的账号信息)
$this->interface->setAuth($this->user['appid'], $this->user['appkey']);
// 获取并验证接口数据
$this->data = $this->interface->getData();
}
/**
* 响应错误消息
*/
public function error($info, $data = '{-null-}', $code = 0): void
{
$this->interface->error($info, $data, $code);
}
/**
* 响应成功消息
*/
public function success($info, $data = '{-null-}', $code = 1): void
{
$this->interface->success($info, $data, $code);
}
}处理业务时,可以对接口提交过来的数据进行二次验证(需要继承基础控制器):
<?php
declare(strict_types=1);
namespace app\data\controller\api;
use think\admin\helper\QueryHelper;
use think\admin\model\SystemUser;
/**
* API 接口控制器示例
* @class Api
* @package app\data\controller\api
*/
class Api extends Auth
{
/**
* 获取用户列表
* 接口地址:/data/api/getUserList
*/
public function getUserList()
{
// 接收并验证参数(从 $this->data 中获取已解析的数据)
$data = $this->_vali([
'page.integer' => '页码格式错误!',
'limit.integer' => '每页数量格式错误!',
'status.in:0,1' => '状态值范围异常!'
], $this->data, [$this, 'error']);
// 使用模型方法进行查询(推荐方式)
$query = SystemUser::mQuery();
$query->where(['is_deleted' => 0])
->like('username,nickname')
->equal('status')
->order('id desc');
// 返回分页查询结果
$this->success("获取成功", $query->page(true, false));
}
/**
* 获取用户详情
* 接口地址:/data/api/getUserInfo
*/
public function getUserInfo()
{
// 验证必填参数
$data = $this->_vali([
'user_id.require' => '用户ID不能为空!',
'user_id.integer' => '用户ID格式错误!'
], $this->data, [$this, 'error']);
// 查询用户信息
$user = SystemUser::mk()->where([
'id' => $data['user_id'],
'is_deleted' => 0
])->find();
if (empty($user)) {
$this->error('用户不存在!');
}
// 返回用户信息
$this->success("获取成功", $user->toArray());
}
/**
* 更新用户状态
* 接口地址:/data/api/updateUserStatus
*/
public function updateUserStatus()
{
// 验证参数
$data = $this->_vali([
'user_id.require' => '用户ID不能为空!',
'status.require' => '状态值不能为空!',
'status.in:0,1' => '状态值范围异常!'
], $this->data, [$this, 'error']);
// 更新用户状态
$result = SystemUser::mk()->where('id', $data['user_id'])->update([
'status' => $data['status'],
'update_at' => date('Y-m-d H:i:s')
]);
if ($result) {
$this->success("更新成功");
} else {
$this->error("更新失败");
}
}
}