整理中
目标:
了解跨域
解决跨域
服务器配置跨域(java, nginx)
前端调试时配置解决跨域
一、什么是跨域
当资源请求来自不同域,协议或端口的资源时,资源会发出跨域HTTP请求。出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。例如,XMLHttpRequest
并且取遵循同源策略。因此,使用XMLHttpRequest
或提取 的Web应用程序只能向自己的域发出HTTP请求。
什么是同源策略
同源策略是一种关键的安全机制,它限制从一个源加载的文档或脚本如何与来自另一个源的资源交互。
浏览器禁止接收信息的目的是为了防止恶意网站从其他网站读取机密信息,但也阻止网站内容合法读取其他网站提供的信息。
同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
举例对比:
http://hotel.jd.com/dir/index.html
url | 是否同源 | 说明 |
---|---|---|
http://hotel.jd.com/dir2/pages.html |
同源 | 仅链接不同 |
http://hotel.jd.com/dir/pages/index.html |
同源 | 仅链接不同 |
https://hotel.jd.com/index.html |
不同源 | 协议不同 |
http://hotel.jd.com:81/pages/index.html |
不同源(IE同源) | 端口不同 |
http://train.jd.com/pages/index.html |
不同源 | 子域名不同 |
http://baidu.com |
不同源 | 主域名不同 |
【注】IE不包括端口到同源检查。因此,https://company.com:81/index.html和https://company.com/index.html被认为是同源的,没有任何限制。这些异常是非标准的,在任何其他浏览器中都不受支持。
浏览器可以链接跨域资源的html元素:
img、script、css、video、audio、object、embed、applet、@font-face、frame、iframe等。
(1)
(2)undefinedundefinedundefined标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。jsonp也用到了。
(3) 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
(4)
(5)
(6)@font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
(7) 和
常见的跨域
接口
main.js:1 Mixed Content: The page at 'https://hotelm-yf.jd.com/coupon' was loaded over HTTPS, but requested an insecure resource 'http://hotel.m.jd.com/api/json/login'. This request has been blocked; the content must be served over HTTPS.
coupon:1 Access to fetch at 'https://hotelm-yf.jd.com/api/wechat/getActivityHotelList' from origin 'http://hotelm-yf.jd.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
[图片上传失败...(image-f3a708-1566981016996)]
[图片上传失败...(image-42aac-1566981016996)]
HTTPS和http跨域提示
[图片上传失败...(image-9c5aa5-1566981016996)]
仅设置了http://hotel.m.jd.com 用https://hotel.m.jd.com时会出现的错误
[图片上传失败...(image-92cc48-1566981016996)]
图片
[图片上传失败...(image-b662f6-1566981016996)]
图片
No 'Access-Control-Allow-Origin' header is present on the requested resource. Originis therefore not allowed access
图片防盗链
会检测访问图片的referer
解决
GET https://csdnimg.cn/pubfooter/images/job-QR.png 403
[图片上传失败...(image-51502b-1566981016996)]
-
跨站请求正常发起,netWork上可以正常查看,但是结果被浏览器拦截了。
跨域的iframe
iframe跨域时仅支持以下JS的api调用
方法 window.blur
window.close
window.focus
window.postMessage
location.replace
属性 window.closed
只读 window.frames
只读 window.length
只读 window.location
读/写. window.opener
只读 window.parent
只读 window.self
只读 window.top
只读 window.window
只读
如何阻止跨域
跨域数据存储
浏览器中存储的数据(如localStorage和IndexedDB)的访问按源分开。每个源都有自己的独立存储,一个源中的JavaScript不能从属于另一个源的存储读写。
cookie使用一个单独的起源定义。页面可以为自己的域或任何父域设置cookie,只要父域不是公共后缀。Firefox和Chrome使用公共后缀列表来确定域是否是公共后缀。Internet Explorer使用自己的内部方法来确定域是否是公共后缀。无论使用哪种协议(HTTP/HTTPS)或端口,浏览器都将使cookie对给定域(包括任何子域)可用。设置cookie时,可以使用域、路径、安全和仅http标志限制其可用性。当您读取cookie时,您无法看到它是从何处设置的。即使您只使用安全的https连接,您看到的任何cookie都可能是使用不安全连接设置的。
二、跨域解决的方式
1.跨域资源共享(CORS)
CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。它允许浏览器向跨域服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
浏览器限制从脚本内发起的跨域HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
跨域资源共享(CORS)机制为Web服务器提供跨域访问控制,从而实现安全的跨域数据传输。现代浏览器在API容器中使用CORS(例如XMLHttpRequest
或Fetch)来缓解跨域HTTP请求的风险。
Access-Control-Allow-Origin
Access-Control-Allow-Origin: *
服务器默认是不被允许跨域的。配置Access-Control-Allow-Origin *
后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。
为了保障系统的安全性,不建议设置为通配 * 。并且这种方式浏览器不能携带cookie信息(携带cookie信息只能使用真实域)。
Access-Control-Allow-Origin也可指定对应的域名,单个或多个,:
Access-Control-Allow-Origin: http://test.blyoo.com,https://www.blyoo.com
Access-Control-Allow-Credentials
响应报头指示的请求的响应是否可以暴露于该页面。当true
值返回时它可以被暴露。
Credentials的凭证是 Cookie ,授权标头或 TLS 客户端证书。
当作为对预检请求的响应的一部分使用时,它指示是否可以使用凭证进行实际请求。请注意,简单的GET
请求不是预检的,所以如果请求使用凭证的资源,如果此资源不与资源一起返回,浏览器将忽略该响应,并且不会返回到 Web 内容。
Access-Control-Allow-Credentials
的 header 文件与该XMLHttpRequest.withCredentials
属性或者在提取 API credentials
的Request()
构造函数中的选项一起工作。必须在双方(Access-Control-Allow-Credentials
的 header 和 XHR 或 Fetch 请求中)设置证书,以使 CORS 请求凭证成功
需要注意的是,在带凭据请求中, Access-Control-Allow-Origin:
头不能是通配符 "*",必须是一个有效的域名。
2.服务端配置:
1.Nginx服务器配置cors
Nginx的配置文件中配置以下参数
add_header Access-Control-Allow-Origin *;
多个指定域名:
add_header Access-Control-Allow-Origin http://test.blyoo.com,https://www.blyoo.com;
The ‘Access-Control-Allow-Origin’ header contains multiple values, but only one is allowed
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
代码中查看是不是配置:
header("Access-Control-Allow-Origin: *");
nginx配置有没有配置:
add_header Access-Control-Allow-Origin *;
这两个配置,只要保留一个即可。
列表配置参考
map corsHost {
default 0;
"~http://www.example.com" http://www.example.com;
"~http://m.example.com" http://m.example.com;
"~http://wap.example.com" http://wap.example.com;
}server
{
listen 80;
server_name www.example2.com;
root /usr/share/nginx/html;
location /
{
add_header Access-Control-Allow-Origin $corsHost;
}
}
其它配置参考:
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
if ("){
set http_origin;
}
if ("){
set http_origin;
}
if ("){
set http_origin;
}
if ("){
set http_origin;
}
add_header 'Access-Control-Allow-Origin' $cors_origin;
2.JAVA中配置cors
如果服务端是Java开发的,添加如下设置允许跨域即可,但是这样做是允许所有域名都可以访问,不够安全。并且这种方式浏览器不能携带cookie信息(携带cookie信息只能使用真实域)。这种方式只推荐在不带cookie信息的开发中测试使用。代码如下:
response.setHeader("Access-Control-Allow-Origin","*");
为保证安全性,可以只添加部分域名允许访问,添加位置可以在下面三处任选一个。
(1)可以在过滤器的filter的dofilter()方法种设置。
(2)可以在servlet的get或者post方法里面设置。
(3)可以放在访问的jsp页面第一行。
使用dofilter()的Demo
public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
// 将ServletResponse转换为HttpServletResponse
HttpServletResponse httpResponse = (HttpServletResponse) res;
// 如果不是80端口,需要将端口加上,如果是集群,则用Nginx的地址,同理不是80端口要加上端口
String [] allowDomain= {"http://www.baidu.com","http://123.456.789.10","http://123.16.12.23:8080"};
Set allowedOrigins= new HashSet(Arrays.asList(allowDomain));
String originHeader=((HttpServletRequest) req).getHeader("Origin");
if (allowedOrigins.contains(originHeader)){
httpResponse.setHeader("Access-Control-Allow-Origin", originHeader);
httpResponse.setContentType("application/json;charset=UTF-8");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");
// 如果要把Cookie发到服务器,需要指定Access-Control-Allow-Credentials字段为true
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Expose-Headers", "*");
}
chain.doFilter(req, res);
}
3.PHP配置cors
//如果需要设置允许所有域名发起的跨域请求,可以使用通配符 *
header("Access-Control-Allow-Origin: *");
?>
//单个
header("Access-Control-Allow-Origin: www.xxx.xom");
?>
//多个
origin);
?>
//多个
_SERVER['HTTP_ORIGIN'], _SERVER['HTTP_ORIGIN']);
}
?>
4.apache配置cors
在httpd配置或.htaccess文件中添加如下语句
SetEnvIf Origin "^(.*.example.com)1
Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
或apache下修改vhosts中根域名的配置:
AllowOverride ALL
Header set Access-Control-Allow-Origin https://www.google.com,https://www.baidu.com
Header add Access-Control-Allow-Origin *
Header add Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header add Access-Control-Allow-Headers "Content-Type"
AllowOverride All
3.前端配置
如仅需要获取接口返回数据,服务端配置好 Access-Control-Allow-Origin 前端即可获取到后端返回的数据了。当跨域还需要传递凭证cookie时。需要在请求时配置Credentials为true。
配置Credentials
以下是前端用各种方式请求时设置Credentials的方式。
1.XHR设置credetials
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
2.Fetch设置携带credentials
默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)
fetch(url, {
credentials: 'include'
})
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, same-origin, *omit
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
3.axios设置credentials
设置axios.defaults.withCredentials
import axios from 'axios'
axios.defaults.withCredentials = true
单独设置
axios({
method: 'get',
url: 'XXX',
withCredentials: true
})
4.vue-resource设置credentials
以下为全局引用vue的demo。非全局引用的参考参数传递。原理一致
VUE官方推荐使用axios ,vue-resource不更新了
import vue from 'vue'
vue.http.get(url,{
credentials: true
},{
emulateJSON: true
})
- 一定要设置 {emulateJSON: true},不然跨域不成功.
5.jQuery ajax设置credentials
$.ajax({
url : 'http://hotel.m.jd.com/api/login',
data : data,
dataType: 'json',
type : 'POST',
xhrFields: {
withCredentials: true
},
crossDomain: true,
contentType: "application/json",
四、前端调试跨域
webpack配置接口地址代理
启动服务器后,本地开发服务下 http://localhost:8080 ,接口地址是 http://www.xxx.com/api/,就会存在跨域的请求,导致接口请求不成功。
webpack自带代理功能,通过以下配置。可以将接口地址代理的方式映射到本地。
解决开发环境的跨域问题(不用在去配置nginx和host)
在webpack.config.js中配置
webpack代理参考:https://webpack.js.org/configuration/dev-server/#devserver-proxy
whistle修改请求头解决跨域
No 'Access-Control-Allow-Origin' header is present on the requested resource
2.jsonp
3.doucument.domain
用document.domain来指定域,主要解决的是子域与父域之间的跨域。
如: hotel.m.jd.com、train.m.jd.com 指定到m.jd.com
4.postMessage()
postMessage()允许来自不同源的脚本采用异步方式进行通信,可以实现跨域消息传递。 postMessage(data,origin) data:要传递的数据 origin:指明目标窗口的源。协议主机端口号 接收消息:监听window的message事件,包括 data——传递过来的message source——发送消息的窗口对象 origin——发送消息窗口的源
5.location.hash
6.script篇
7.window.name
window.name 传输技术,原本是 Thomas Frank 用于解决 cookie 的一些劣势(每个域名 4 x 20 Kb的限制、数据只能是字符串、设置和获取 cookie 语法的复杂等等)而发明的。后来 Kris Zyp 在此方法的基础上强化了 window.name 传输 ,用来解决跨域数据传输问题。
window.name 的美妙之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)
在A域名下设置window.name,通过href跳转至目标页。 再获取window.name的参数
例:
A页面
window.name = '跨域的参数'
点击我跨域传值
B页面
window.name //跨域的参数
参考文献
-
同源策略https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
HTTP访问控制(CORS)https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
Server-Side Access Controlhttps://developer.mozilla.org/zh-CN/docs/Web/HTTP/Server-Side_Access_Control
XY博客https://www.cnblogs.com/sunmmi/articles/5956554.html
Fetch https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
webpack proxy:https://webpack.js.org/configuration/dev-server/#devserver-proxy
crossorigin = "anonymous"
七种跨域方法【2.document.domain篇】