🔐 签名接口

ThinkAdmin 提供完善的签名接口机制,支持安全的 API 接口调用和数据验证。

🚀 主要功能

  • 安全签名: 基于 MD5 的接口数据签名机制
  • 时间验证: 支持时间戳验证,防止重放攻击
  • 随机字符串: 使用随机字符串增强安全性
  • 调试模式: 支持调试模式,方便开发测试
  • 分页支持: 支持列表分页功能
  • 文件传输: 支持 base64 文件传输

📋 请求字段说明

必需字段

  • appid: 接口请求认证账号,需要保持两端一致
  • time: 接口请求时间(时差不能超过30秒),使用时间戳,单位为秒
  • nostr: 接口请求随机字符串,可以使用随机函数生成
  • data: 接口请求数据(数据需转为JSON格式),需以原格式传输
  • sign: 接口数据签名,按特定规则生成

签名规则

  • 字段顺序: 依次为 appiddatatimeappkeynostr
  • 连接方式: 五个字段以 # 连接
  • 加密方式: 进行 MD5 编码获取签名
  • 私钥保护: appkey 为接口私钥,仅用于签名且不参与接口数据请求

⚙️ 特殊说明

文件传输

  • 传输方式: 使用 base64 传输文件
  • 数据格式: 需要去掉数据头部
  • 大文件处理: 建议直接对接云存储
  • 性能考虑: 大文件传输可能影响性能

调试模式

  • 调试开关: 当接口的 debugtrue
  • 简化请求: 不需要传签名内容
  • 普通表单: 只需要以普通表单传原接口的 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("更新失败");
        }
    }
}
最近更新:
Contributors: 邹景立, Anyon