跳到主要内容

Upload - PHP

使用 PHP 编程语言编写的请求示例。


<?php

namespace Test\Demo;

use App\Util\RsaUtil;
use Exception;
use PHPUnit\Framework\TestCase;
use function PHPUnit\Framework\assertEquals;

/**
* 模拟 agency 向 ice-run 发送请求
*/
class FireFileTest extends TestCase
{

/**
* 服务商(agency)的编号
* 此处使用 agency_no = 100000000000000000 作为模拟
* 真实请求场景,请咨询 ice-run 的技术支持获取正式的 服务商编号 agency_no
*/
private static string $AGENCY_NO = "100000000000000000";

/**
* 安全算法 RSA
*/
private static string $SECURITY = "RSA";

/**
* ice-run 的 公钥
*/
private static string $PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC78QqLKTkVcwvB2Scm4IoImOXmIthVmx4HHr5Im09aol2r2UlMK3jl4lamdxktvjFswWx41hQBD4tyYFHTY+mQJsRK8Zwm7LZmLJDHcgibIuLxEddln+MI+rWwT+SOdOt7lBvd9PcO4nYr/3Nqkyk8L4PrT+GF7dpVk8iE/VpTwQIDAQAB";

/**
* agency 的 私钥
*/
private static string $PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOyC76Y+/2NqkkIVUtAHkbZkBYI2XXkuqE9o4oIDkbyAY04jC5MLa3oVc3uGG7T/Elodo9dIbIxNh7/ir7GQLR/hY+ookHAewurJ1z4Jn5TQeRk3+nYQcX6a6orfUOMffwcn0Jtat8D+cNAu3uCi+N7Ez/yNpItNORYqTa8AkgzrAgMBAAECgYAS/rsfl3ysb+E6RHsnsQvvYZ4dpJ8iPfCPnCVg+sdoI8mV+3ORBkBGCFYDjDRKd5fyO+IuRqdNJ2bpLtwcfy9YchcV/x40GIuAlre6A6qt4Raq+0l4FJufaqgdmSiLxd5zPX79gdMvBqTbdYzCSDGP5Pf3pTqIS3iKvSixFTQBzQJBAO6k6rE6JE/EHrbRjVtZNitDZ8rouCL0s9nGsgIDBSfgOCClO+HlZKcHq6W9ry8j1gOFeGH2rF1yMslA/ZymRL0CQQD9tk/gk9Xs1Eg1if7SLwvNxUP+z96oEqQTF26tSudPRwOO88fnfE4ZZ4heBt1QI+s7IhG39qecsFW269nvKfbHAkEArXmEgUBalQFjslGyB+1Zyyk8keuJrx9ifbRKQdwgK1R6eICkfxlZiXGx/NFeP041jGnBkLTXpzYUZOexc+YJoQJBANR3r87vnxAU+l+zr62O7oCk+XtT0y/HZJYEYpBHEQyn+MfnSXqG89R8iovLjd0GJ4E+173KlrU2SqHEQ57w8pMCQAEoDjFUXpYzBl8e0M9XsSpGY531mbOl8ASnVp4OSyavaCsn56eEkL9TI5q69KrYUEA4PN3BazMWfll2s/wu6G0=";

/**
* ice-run 的 test 环境的 API 网关 的域名地址
* 正式生产环境的域名地址请查阅 ice-run 的技术文档
*/
private static string $DOMAIN = "test-fire-api.ice.run";

/**
* 文件上传接口地址
*/
private static string $UPLOAD = "/file-service/api/upload";

/**
* 文件上传接口 upload
* @throws Exception
*/
function test_upload(): void
{

/**
* 对方公钥
*/
$public_key = RsaUtil::public_key(self::$PUBLIC_KEY);

/**
* 己方私钥
*/
$private_key = RsaUtil::private_key(self::$PRIVATE_KEY);

/**
* 此处使用 `${URL}` 模拟请求地址
*/
$url = "https://" . self::$DOMAIN . self::$UPLOAD;
echo "request url : " . $url . "\n";

/**
* 读取文件内容
*/
$filename = "/tmp/0.txt";
$hash_plains = hash_file('sha256', $filename);

/**
* hash 密文:agency 使用 ice-run 公钥 对 hash 明文加密后得到的密文
*/
$hash_cipher = RsaUtil::encrypt($public_key, $hash_plains);
echo "hash cipher : " . $hash_cipher . "\n";

/**
* 请求签名:agency 使用 私钥 对 hash 密文进行签名
* 当 ice-run 收到请求时,将会使用 agency 公钥 进行验签
*/
$request_sign = RsaUtil::sign($private_key, $hash_cipher);
echo "request sign : " . $request_sign . "\n";

/**
* 请求时间,当前世界标准时间的字符串,格式为 RFC-1123 标准,服务器会校验时间差,不允许超过设置的时间间隔(默认为 2 分钟)
*/
$time = gmdate("D, d M Y H:i:s T");

/**
* 链路追踪号(防止重放攻击的请求编号)
* 单位时间内(默认为 1 分钟)不重复的字符串,建议使用 uuid 或者时间戳+随机数。限制长度 = 32。正则表达式 = `^[0-9a-f]{32}$`
*/
$trace = bin2hex(random_bytes(16));

/**
* 请求头
*/
$request_headers = array(
// "Content-Type: multipart/form-data",
"X-Security: " . self::$SECURITY,
"X-Client: " . self::$AGENCY_NO,
"X-Hash:" . $hash_cipher,
"X-Sign: " . $request_sign,
"X-Time: " . $time,
"X-Trace: " . $trace
);
echo "request headers : " . json_encode($request_headers) . "\n";

/**
* 请求数据 request body
*/
$curl_file = curl_file_create($filename);
$request_body = array(
"hash" => $hash_plains,
"file" => $curl_file
);

/**
* http 客户端,此处使用 php 标准库 curl 扩展 模拟,实际场景建议自行选择 HTTP 客户端组件包
* 发起 HTTP 请求之前,请确认 `${DOMAIN}` 可以正常访问,部分客户端的服务器环境可能需要设置请求代理
*/
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request_body);

$response = curl_exec($ch);

if ($response === false) {
$curl_errno = curl_errno($ch);
$curl_error = curl_error($ch);
curl_close($ch);

self::fail("cURL request failed with error ($curl_errno): $curl_error");
}

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$response_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

/**
* 响应状态码
*/
echo "response status : " . $response_status . "\n";
assertEquals(200, $response_status);

/**
* 响应 header
*/
$headers = substr($response, 0, $header_size);
$headers_array = explode("\r\n", $headers);
$response_headers = [];
foreach ($headers_array as $header) {
$header_parts = explode(": ", $header, 2);
if (count($header_parts) == 2) {
$response_headers[$header_parts[0]] = $header_parts[1];
}
}
echo "response headers : " . json_encode($response_headers) . "\n";

/**
* 响应 header X-Sign
*/
$response_sign = $response_headers["X-Sign"];
echo "response sign : " . $response_sign . "\n";

/**
* 响应 body ,json 格式的字符串
*/
$response_body = substr($response, $header_size);
echo "response body : " . $response_body . "\n";

/**
* 响应数据 response body
* 此处使用 json 对象 模拟,实际场景建议定义数据模型类
* 数据的 键 有 code 和 message 和 data
* 其中 data 为 json 序列化后的字符串 RSA 加密后的密文
*/
$response_object = json_decode($response_body);

/**
* 响应码,默认 000000 表示请求成功
*/
$code = $response_object->{"code"};
assertEquals("000000", $code);

/**
* 响应 数据密文
*/
$data_cipher = $response_object->{"data"};

/**
* 响应验签:ice-run 处理响应时,会使用 ice-run 私钥对响应密文进行签名
* agency 收到响应后,需要使用 ice-run 公钥进行验签
*/
$verify = RsaUtil::verify($public_key, $data_cipher, $response_sign);
echo "sign verify : " . $verify . "\n";
assertEquals(true, $verify);

/**
* 响应数据明文:ice-run 使用 agency 公钥 对响应数据密文进行解密
*/
$data_plains = RsaUtil::decrypt($private_key, $data_cipher);
echo "data plains : " . $data_plains . "\n";

}

}