本地跨域与服务器跨域
前端页面与后台运行在不同的服务器或不同的域名时,就会出现跨域问题。
本地跨域:
如果是同一台服务器,前后端url拥有不同的域名,则通过后端设置正确的请求体origin则可解决:
(为了安全起见,前端不得修改请求体的 Origin域名)
//定义响应头信息
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Content-Type, Origin, X-Requested-With, Accept');
header('Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS');
header('Access-Control-Max-Age: 3600');
header('Access-Control-Allow-Origin:' . '前端域名网址');
服务器跨域:
目前解决这类跨域问题主要有以下方法:
不讨论只能使用GET的jsonp方法。
方法一、CROS(跨域资源共享,Cross-Origin Resource Sharing)的方法。
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)跨域资源共享 CORS 详解。看名字就知道这是处理跨域问题的标准做法。CORS有两种请求,简单请求和非简单请求。
简单请求下, 可以使用:
header("Access-Control-Allow-Origin", "*"); //表示允许任何远程访问
设置Access-Control-Allow-Methods //允许访问的方法等
非简单请求:
如果发送的是带凭据的非简单请求,但服务器的相应中没有包含这个头部,那么浏览器就不会把相应交给JavaScript,请求就无法得到结果的数据(浏览器得到了,但是我们请求的方法得不到,因为被浏览器拦截了),因此在withCredentials时,服务端的Access-Control-Allow-Origin必须配置具体的具体的域名, 不能为通配符。并且还需要设置其他的请求头:
CROS协议流程
1、设置HTTP响应头Access-Control-Allow-Origin,指定服务器端允许进行跨域资源访问的来源域。
2、预请求验证:
浏览器在发现页面中有上述条件的动态跨域请求的时候,并不会立即执行对应的请求代码,而是会先发送Preflighted requests(预先验证请求)
OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。
服务器收到OPTIONS请求后,设置Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers头部与浏览器沟通来判断是否允许这个请求。
3、服务端流程[重要]
要实现CORS跨域,服务端需要这个一个流程:服务端流程
流程如下:
首先查看http头部有无origin字段;
如果没有,或者不允许,直接当成普通请求处理,结束;
如果有并且是允许的,那么再看是否是preflight(method=OPTIONS);
如果是preflight,就返回Allow-Headers、Allow-Methods等,内容为空;
如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常内容。
服务端响应流程配置, 参考以下在过滤器中间层中处理的配置:
header('Access-Control-Allow-Credentials: true'); // 非简单请求下的配置处理
header('Access-Control-Allow-Headers: Origin, Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
header('Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS');
header('Access-Control-Max-Age: 3600');
header('Access-Control-Allow-Origin: ' . Request::instance()->header('Origin'));
//非简单请求下, 指定获取到的单个域名[间接通配], 此处加白名单组校验更好。
对于预请求的处理可以有以下几种方法:
1、JAVA的拦截器或者PHP中入口文件对OPTIONS请求进行拦截return
2、NIGNX在流程中统一处理
NIGNX对以上流程实现以及OPTIONS请求处理配置:参考
下面nginx-spdy-push里/pub接口启用CORS的配置:
location ~ ^/pub/([-_.A-Za-z0-9]+)$ {
set $cors "local";
# configure CORS based on https://gist.github.com/alexjs/4165271
# (See: http://www.w3.org/TR/2013/CR-cors-20130129/#access-control-allow-origin-response-header )
if ( $http_origin ~* "https://.+\.gw\.com\.cn(?=:[0-9]+)?" ) {
set $cors "allow";
}
if ($request_method = "OPTIONS") {
set $cors "${cors}options";
}
# if CORS request is not a simple method
if ($cors = "allowoptions") {
# Tells the browser this origin may make cross-origin requests
add_header 'Access-Control-Allow-Origin' "$http_origin";
# in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies)
add_header 'Access-Control-Allow-Credentials' "true";
# === Return special preflight info ===
# Tell browser to cache this pre-flight info for 1 day
add_header 'Access-Control-Max-Age' 86400;
# Tell browser we respond to GET,POST,OPTIONS in normal CORS requests.
# Not officially needed but still included to help non-conforming browsers.
# OPTIONS should not be needed here, since the field is used
# to indicate methods allowed for 'actual request' not the preflight request.
# GET,POST also should not be needed, since the 'simple methods' GET,POST,HEAD are included by default.
# We should only need this header for non-simple requests methods (e.g., DELETE), or custom request methods (e.g., XMODIFY)
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
# Tell browser we accept these headers in the actual request
add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
# === response for OPTIONS method ===
# no body in this response
add_header 'Content-Length' 0;
# (should not be necessary, but included for non-conforming browsers)
add_header 'Content-Type' 'text/plain, charset=utf-8';
# indicate successful return with no content
return 204;
}
if ($cors = "allow") {
rewrite /pub/(.*) /pub_cors/$1 last;
}
if ($cors = "local") {
rewrite /pub/(.*) /pub_int/$1 last;
}
}
location ~ /pub_cors/(.*) {
internal;
# Tells the browser this origin may make cross-origin requests
add_header 'Access-Control-Allow-Origin' "$http_origin";
# in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies)
add_header 'Access-Control-Allow-Credentials' "true";
push_stream_publisher admin; # enable delete channel
set $push_stream_channel_id $1;
push_stream_store_messages on; # enable /sub/ch.b3
push_stream_channel_info_on_publish on;
}
location ~ /pub_int/(.*) {
# internal;
push_stream_publisher admin; # enable delete channel
set $push_stream_channel_id $1;
push_stream_store_messages on; # enable /sub/ch.b3
push_stream_channel_info_on_publish on;
}
方法二、NIGNX反向代理解决跨域:
[CORS] 需要服务器设置header :Access-Control-Allow-Origin。
客户端AJAX额外携带参数,而
NIGNX反向这个方法一般很少有人提及,但是他可以不用目标服务器配合,不过需要你搭建一个中转nginx服务器,用于转发请求。
nginx配置
关于nginx的配置可以查看另一篇博文:http://www.cnblogs.com/renjing/p/6126284.html。找到nginx的配置文件“nginx.conf”,修改一下信息
server {
listen 80; #监听80端口,可以改成其他端口
server_name localhost; # 当前服务的域名
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://localhost:81;
proxy_redirect default;
}
location /apis { #添加访问目录为/apis的代理配置
rewrite ^/apis/(.*)$ /$1 break;
proxy_pass http://localhost:82;
}
#以下配置省略
配置解释:
1.由配置信息可知,我们让nginx监听localhost的80端口,网站A与网站B的访问都是经过localhost的80端口进行访问。
2.我们特殊配置了一个“/apis”目录的访问,并且对url执行了重写,最后使以“/apis”开头的地址都转到“http://localhost:82”进行处理。
3.rewrite ^/apis/(.)1 break;
代表重写拦截进来的请求,并且只能对域名后边以“/apis”开头的起作用,例如www.a.com/apis/msg?x=1重写。只对/apis重写。
rewrite后面的参数是一个简单的正则 ^/apis/(.)1代表正则中的第一个(),$2代表第二个()的值,以此类推。
break代表匹配一个之后停止匹配。
4.经过重写后的请求,都会被认为是同源请求,从而不会导致跨域问题。
—————————————————————————————————————
cookie共享设置
一、将cookie的domain设置为多个/共同的顶级域名
二、带认证的请求
默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
在ajax里带上参数:
1 crossDomain: true,
2 xhrFields:{
3 withCredentials:true
4 },
如果服务器接收带凭据的请求,会用下面的HTTP头部来响应。
在服务端设置:
Access-Control-Allow-Credentials: true
服务器还可以在Preflight响应中发送这个HTTP头部,表示允许源发送带凭据的请求。