示例代理:http://ce.monihe.com/proxy.php
<?php
/**
* 如意册 AJAX跨域请求【代理】功能示范
* 注意:
* !此代理$alow_domains指定允许代理的域名列表,请添加您的域名或关闭此安全设置;
* 此代理支持files($_FILES)的代理上传提交,但需要PHP5.6及以上版本;;
* 但不支持header头的代理提交;
* 使用代理调试、测试目标请求,会增加更多的错误因素(如:header、cookie、referer、编码、跨域 等);
* 此代理程序仅供参考、示范;
* 推荐尽量使用直接跨域请求的方案;
* @author libowen
* @for 如意册 http://ruyice.com/
* @date 2017-11
*/
define('APP_DEBUG', true);
if(APP_DEBUG){
// 注意:跨域请求必须的设置(允许从指定域名发来的请求)
$origin = !empty($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : (!empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
if($origin){
$ruyice_domain = 'ruyice.com'; // 只允许从指定域名发来的请求
if($_SERVER['HTTP_HOST'] != $ruyice_domain){
$parseUrl = parse_url($origin);
if($ruyice_domain === $parseUrl['host']){
// 必须包括协议scheme
$allow_origin = $parseUrl['scheme'] . '://' . $parseUrl['host'];
// 允许指定域名发来的访问请求,即固定限制为客户端网址
header("Access-Control-Allow-Origin: " . $allow_origin);
header("Access-Control-Allow-Methods: POST,GET,PUT,OPTIONS,DELETE");
// 是否允许请求带有验证信息,如cookie
header("Access-Control-Allow-Credentials: true");
if('OPTIONS' === strtoupper($_SERVER['REQUEST_METHOD'])){
// 支持的请求header字段名
header("Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type, SessionID,ASPSESSIONID,ASP.NET_SessionId,JsessionId,PHPSESSID,Authorization,access-token,RuYiCe-DIY-Allow"); // 允许自定义的头部,以逗号隔开,不含下划线,大小写不敏感(RuYiCe-DIY-Allow 可更换为需要ajax发送的header字段名,多个则英文逗号分隔,否则浏览器会屏蔽掉返回的结果)
header("HTTP/1.1 204 Accepted");
exit;
}
/*
// 开放式开发、测试时,可考虑加个header安全验证
$access_key = 'access-token'; // 自定义(但要在请求的header中包含,且服务器端Access-Control-Allow-Headers包含)
$access_password = 'access-token-value';
$access_key = strtoupper(str_replace('-', '_', $access_key));
if($access_password !== $_SERVER['HTTP_' . $access_key]){
header("Content-type: text/html; charset=utf-8");
echo json_encode([
'code' => 0,
'msg' => 'access-token not right! header头的安全验证错误!',
'data' => []
]);
exit;
}
*/
// 设置为ajax请求方式
if($_SERVER['HTTP_HOST'] != $ruyice_domain){
$_SERVER['HTTP_' . 'X_REQUESTED_WITH'] = 'XMLHttpRequest';
}
} else {
// 非指定域名发来的,则忽略上面的设置
}
}
}
}
// header("content-Type: text/html; charset=UTF-8");
$is_post = false;
if (!empty($_POST)) {
$is_post = true;
}
$url = (!empty($_GET['source_url']) ? urldecode($_GET['source_url']) : (!empty($_GET['url']) ? urldecode($_GET['url']) : ''));
if (empty($url)) {
if ($is_post) {
$url = (!empty($_POST['source_url']) ? $_POST['source_url'] : (!empty($_POST['url']) ? $_POST['url'] : ''));
}
}
if (empty($url)) {
$return = [
'error_code' => 10001,
'msg' => '未指定url'
];
exit_return($return);
}
// 指定允许的域名,非指定域名则中断并提示
$alow_domains = ['ce.monihe.com', 'ce.dev.monihe.com', 'yourdomain',
'suggest.taobao.com', 'www.kuaidi100.com', 'baike.baidu.com', 'api.map.baidu.com',
'm.weather.com.cn', 'php.weather.sina.com.cn', 'qzone-music.qq.com', 'v5.pc.duomi.com',
'cgi.music.soso.com', 'v.youku.com', 'cache.video.iqiyi.com', 'api.tudou.com',
'www.tudou.com', 'gc.ditu.aliyun.com', 'www.telize.com', 'ditu.amap.com',
'int.dpool.sina.com.cn', 'ip.taobao.com', 'tcc.taobao.com', 'virtual.paipai.com',
'www.baifubao.com', 'cz.115.com', 'www.youdao.com', 'api.showji.com', 'dict.qq.com',
'r.qzone.qq.com', 'ajax.googleapis.com'];
$url_host = parse_url($url)['host'];
if(!in_array($url_host, $alow_domains)){
$return = [
'error_code' => 10002,
'msg' => '指定被代理的url不在允许范围!请把 '.$url_host.' 添加到代理程序的$alow_domains数组',
'source_url' => parse_url($url),
'allow_domains' => isset($alow_domains)?$alow_domains:null
];
exit_return($return);
}
if (!$is_post) {
if (strpos($url, '?') === false) {
$url .= '?';
}
$get_data = $_GET;
if($get_data){
unset($get_data['source_url']);
// unset($get_data['url']);
$url .= '&' . http_build_query($get_data);
}
}
// 提交登录表单请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 设置curl执行超时时间最大是多少
// 伪造网页来源地址
//curl_setopt($ch, CURLOPT_REFERER, "http://ruyice.com");
$headers = [
'X-Requested-With: XMLHttpRequest' // header头设置此请求为ajax请求
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// 获取的内容不直接输出,而作为变量
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($is_post) {
$post_data = $_POST;
curl_setopt($ch, CURLOPT_POST, true);
if(empty($_FILES)) { // 无文件上传提交
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
} else { // 含文件上传提交
// header("content-Type: text/html; charset=UTF-8");
// $return = [
// 'error_code' => 10003,
// 'msg' => '此代理不支持文件上传提交!',
// 'source_url' => parse_url($url),
// '$_POST' => $_POST,
// '$_FILES' => $_FILES
// ];
// exit_return($return);
// 限制文件数量和大小 file_limit=5,max_size=512000
$is_error = 0;
$file_limit = 5;
$max_size = 512000;
$file_count = 0;
$files_data = [];
foreach($_FILES as $key => $value){
if(is_array($value['tmp_name'])){
$temp_count = count($value['tmp_name']);
for($i=0; $i<$temp_count; $i++){
$file_count++;
if($value['error'][$i] > 0){
$is_error = 1;
break;
}
if($value['size'][$i] > $max_size){
$is_error = 2;
break;
}
// PHP版本5.6及以上,curl文件上传必须使用CurlFile对象
$files_data[$key.'['.$i.']'] = new \CURLFile(realpath($value['tmp_name'][$i]), $value['type'][$i], $value['name'][$i]);
}
} else {
$file_count++;
if($value['error'] > 0){
$is_error = 1;
break;
}
if($value['size'] > $max_size){
$is_error = 2;
break;
}
// PHP版本5.6及以上,curl文件上传必须使用CurlFile对象
$files_data[$key] = new \CURLFile(realpath($value['tmp_name']), $value['type'], $value['name']);
}
if($file_count > $file_limit){
$return = [
'error_code' => 10004,
'msg' => '已超过代理程序允许上传文件数量:' . $file_limit,
'$_FILES' => $_FILES
];
exit_return($return);
}
if($is_error === 1){
$return = [
'error_code' => 10005,
'msg' => '提交到代理程序的文件有错误!',
'$_FILES' => $_FILES
];
exit_return($return);
} elseif($is_error === 2){
$return = [
'error_code' => 10006,
'msg' => '提交到代理程序的文件太大!',
'$_FILES' => $_FILES
];
exit_return($return);
} else {}
}
// exit_return($files_data);
$post_data = array_merge($post_data, $files_data);
curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
}
// 注意:不同服务器端开发语言cookie对应session的id名是不同的:SessionID,ASPSESSIONID,ASP.NET_SessionId,JsessionId,PHPSESSID
$lang_config = [
'php' => 'PHPSESSID',
'asp' => 'ASPSESSIONID',
'aspx' => 'ASP.NET_SessionId',
'asp.net' => 'ASP.NET_SessionId',
'jsp' => 'JsessionId',
'default' => 'SessionID',
];
$url_lang = 'php'; // 被代理地址的服务器端开发语言对应的配置
// 设置cookie
$cookie = '';
////foreach($_COOKIE as $key => $value){【注意:高危使用,请谨慎使用$_COOKIE】
//// if($key == 'PHPSESSID'){
//// $key = $lang_config[$url_lang];
//// }
//// $cookie.= $key . '=' . $value . ';';
////}
$session_id = substr(md5(date('Y-m-d')), 0, 26); // 请自定义
foreach ($lang_config as $value){
$cookie.= $value . '=' . $session_id . ';';
}
if($cookie){
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
}
// 参数为0表示不带头文件,为1表示带头文件
curl_setopt($ch, CURLOPT_HEADER, false);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
exit;
function exit_return($return_data)
{
header("content-Type: text/html; charset=UTF-8");
echo json_encode($return_data);
exit;
}