Server-Side Access Control
服务器端访问控制
浏览器发送特定的HTTP标头,用于从内部XMLHttpRequest
或获取API发起的跨站点请求。它还希望看到具有跨站点响应的特定HTTP标头。HTTP访问控制(CORS)文章中提供了这些头文件的概述,包括启动请求和处理来自服务器的响应的示例JavaScript代码以及对每个头文件的讨论,应该将其作为配套文章这个。本文介绍处理访问控制请求
和制定访问控制响应
在PHP中。本文的目标受众是服务器程序员或管理员。尽管这里显示的代码示例是用PHP编写的,但类似的概念适用于ASP.net,Perl,Python,Java等。通常,这些概念可以应用于处理HTTP请求并动态地制定HTTP响应的任何服务器端编程环境。
讨论HTTP标头
这篇文章涵盖了客户端和服务器使用的HTTP标头,并且应该被视为读取前提条件。
工作代码示例
后续章节中的PHP片段(以及对服务器的JavaScript调用)取自此处发布的工作代码示例。这些将在实现跨站点的浏览器中工作XMLHttpRequest
。
简单的跨网站请求
简单访问控制请求在以下情况下启动:
- 一个HTTP/1.1
GET
或者一个POST
被用作请求方法。在POST
的情况下,Content-Type
请求体的是一种application/x-www-form-urlencoded
,multipart/form-data
或text/plain.
- 没有自定义标头与HTTP请求一起发送(例如
X-Modified
等)
在这种情况下,可以根据一些考虑将回复发送回去。
- 如果有问题的资源被广泛访问(就像GET访问的任何HTTP资源一样),那么发送回头
Access-Control-Allow-Origin: *
就足够了,除非
资源需要诸如Cookie和HTTP认证信息之类的凭证。
- 如果资源应该基于请求者域保持限制,
或者
如果资源需要使用凭据访问(或设置凭证),则Origin
可能需要按请求头进行过滤,或者
至少回应请求者Origin
(例如Access-Control-Allow-Origin:
http://arunranga.com
)。此外,Access-Control-Allow-Credentials: true
标题将不得不发送。这将在后面的章节中讨论。
简单访问控制请求部分显示客户端和服务器之间的头部交换。这是处理简单请求的PHP代码段:
<?php
// We'll be granting access to only the arunranga.com domain
// which we think is safe to access this resource as application/xml
if($_SERVER['HTTP_ORIGIN'] == "http://arunranga.com") {
header('Access-Control-Allow-Origin: http://arunranga.com'
header('Content-type: application/xml'
readfile('arunerDotNetResource.xml'
} else {
header('Content-Type: text/html'
echo "<html>";
echo "<head>";
echo " <title>Another Resource</title>";
echo "</head>";
echo "<body>",
"<p>This resource behaves two-fold:";
echo "<ul>",
"<li>If accessed from <code>http://arunranga.com</code> it returns an XML document</li>";
echo "<li>If accessed from any other origin including from simply typing in the URL into the browser's address bar,";
echo "you get this HTML document</li>",
"</ul>",
"</body>",
"</html>";
}
?>
上面的内容检查Origin
浏览器发送的头文件(通过$ _SERVER'HTTP_ORIGIN'获得)是否匹配' http://arunranga.com
'。如果是,它会返回Access-Control-Allow-Origin:
http://arunranga.com
。这个例子可以在这里看到。
预先请求的请求
预冲的访问控制请求在下列情况下发生:
- 以外的方法,
GET
或POST
使用,或如果POST
使用具有Content-Type
比其它
的一个application/x-www-form-urlencoded
,multipart/form-data
或text/plain
。举例来说,如果Content-Type
所述的POST
体是application/xml
,请求预检。
- 自定义标题(例如
X-PINGARUNER
)与请求一起发送。
Preflighted访问控制请求部分显示客户端和服务器之间的头部交换。响应预检请求的服务器资源需要能够做出以下决定:
- 基于
Origin
(如果有的话)过滤。
- 响应于
OPTIONS
请求(这是预检请求),包括与发送必要的值Access-Control-Allow-Methods
,Access-Control-Allow-Headers
(如果需要,为了任何附加头的应用程序的工作),并且,如果证书是必要的这一资源,Access-Control-Allow-Credentials
。
- 对实际请求的回应,包括处理
POST
数据等。
以下是处理预发光请求的PHP示例:
<?php
if($_SERVER['REQUEST_METHOD'] == "GET") {
header('Content-Type: text/plain'
echo "This HTTP resource is designed to handle POSTed XML input";
echo "from arunranga.com and not be retrieved with GET";
} elseif($_SERVER['REQUEST_METHOD'] == "OPTIONS") {
// Tell the Client we support invocations from arunranga.com and
// that this preflight holds good for only 20 days
if($_SERVER['HTTP_ORIGIN'] == "http://arunranga.com") {
header('Access-Control-Allow-Origin: http://arunranga.com'
header('Access-Control-Allow-Methods: POST, GET, OPTIONS'
header('Access-Control-Allow-Headers: X-PINGARUNER'
header('Access-Control-Max-Age: 1728000'
header("Content-Length: 0"
header("Content-Type: text/plain"
//exit(0
} else {
header("HTTP/1.1 403 Access Forbidden"
header("Content-Type: text/plain"
echo "You cannot repeat this request";
}
} elseif($_SERVER['REQUEST_METHOD'] == "POST") {
// Handle POST by first getting the XML POST blob,
// and then doing something to it, and then sending results to the client
if($_SERVER['HTTP_ORIGIN'] == "http://arunranga.com") {
$postData = file_get_contents('php://input'
$document = simplexml_load_string($postData
// do something with POST data
$ping = $_SERVER['HTTP_X_PINGARUNER'];
header('Access-Control-Allow-Origin: http://arunranga.com'
header('Content-Type: text/plain'
echo // some string response after processing
} else {
die("POSTing Only Allowed from arunranga.com"
}
} else {
die("No Other Methods Allowed"
}
?>
请注意,为了响应OPTIONS
预检和POST
数据,将返回相应的标题。因此一个资源处理预检和实际请求。在对OPTIONS
请求的响应中,服务器通知客户端实际的请求确实可以用该POST
方法进行,并且诸如头部字段X-PINGARUNER
可以与实际请求一起发送。这个例子可以在这里看到。
认证请求
凭证访问控制请求 - 即伴随有Cookie或HTTP身份验证信息的请求(并且期望Cookie与响应一起发送) - 可以是简单或预检,具体取决于所使用的请求方法。
在简单请求方案中,请求将以Cookie发送(例如,如果withCredentials
标志设置为开启XMLHttpRequest
)。如果服务器响应Access-Control-Allow-Credentials: true
附加的凭证响应,则响应被客户端接受并暴露给Web内容。在预检请求,服务器可以响应Access-Control-Allow-Credentials: true
的OPTIONS
请求。
以下是一些处理凭证请求的PHP:
<?php
if($_SERVER['REQUEST_METHOD'] == "GET") {
header('Access-Control-Allow-Origin: http://arunranga.com'
header('Access-Control-Allow-Credentials: true'
header('Cache-Control: no-cache'
header('Pragma: no-cache'
header('Content-Type: text/plain'
// First See if There Is a Cookie
if (!isset($_COOKIE["pageAccess"])) {
setcookie("pageAccess", 1, time()+2592000
echo 'I do not know you or anyone like you so I am going to';
echo 'mark you with a Cookie :-)';
} else {
$accesses = $_COOKIE['pageAccess'];
setcookie('pageAccess', ++$accesses, time()+2592000
echo 'Hello -- I know you or something a lot like you!';
echo 'You have been to ', $_SERVER['SERVER_NAME'], ';
echo 'at least ', $accesses-1, ' time(s) before!';
}
} elseif($_SERVER['REQUEST_METHOD'] == "OPTIONS") {
// Tell the Client this preflight holds good for only 20 days
if($_SERVER['HTTP_ORIGIN'] == "http://arunranga.com") {
header('Access-Control-Allow-Origin: http://arunranga.com'
header('Access-Control-Allow-Methods: GET, OPTIONS'
header('Access-Control-Allow-Credentials: true'
header('Access-Control-Max-Age: 1728000'
header("Content-Length: 0"
header("Content-Type: text/plain"
} else {
header("HTTP/1.1 403 Access Forbidden"
header("Content-Type: text/plain"
echo "You cannot repeat this request";
}
} else {
die("This HTTP Resource can ONLY be accessed with GET or OPTIONS"
}
?>
请注意,对于有证书请求的情况,Access-Control-Allow-Origin:
标头不得
有通配符值“*”。它必须
提到一个有效的原始域。上面的例子可以看到在这里运行。
Apache示例
限制对某些URI的访问
一个有用的技巧是使用Apache重写,环境变量和标头来应用于Access-Control-Allow-*
某些URI。例如,这对于将GET /api(.*).json
请求的跨请求限制为没有凭证的请求很有用:
RewriteRule ^/api(.*)\.json$ /api$1.json [CORS=True]
Header set Access-Control-Allow-Origin "*" env=CORS
Header set Access-Control-Allow-Methods "GET" env=CORS
Header set Access-Control-Allow-Credentials "false" env=CORS
另请参阅
- HTTP Access Control covering the HTTP headers
XMLHttpRequest