所谓跨域,就是JS跨域,JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。
这里介绍一下服务端跨域的总结
参考教程:
https://spring.io/guides/gs/rest-service-cors/
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html
所谓跨域,就是JS跨域,JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。
首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js | 同一域名下 | 允许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js | 同一域名下不同文件夹 | 允许 |
http://www.a.com:8000/a.js http://www.a.com/b.js | 同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js | 主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js | 同一域名,不同二级域名(同上) | 不允许(cookie这种情况下也不允许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js | 不同域名 | 不允许 |
特别注意两点:
第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
解决的方式有好几种:
- 服务端nginx配置
- JSONP
- CORS
下面会进行介绍。
环境搭建,两台服务器分别安装了nginx(openresty)和JDK8.
首先有两个域名:
192.168.15.21 cluster1.zhangdemo.com
192.168.15.22 cluster2.zhangdemo.com
本机是192.168.15.1,nginx配置为:
1 | #user nobody; |
另外一台配置相仿,该配置的主要作用是将机器的80端口反向代理到8080端口上。
创建一个只有web模块的spring boot项目:web-app0
创建一个Greetting的类:
1 | package com.crossdomain.webapp0.beans; |
创建一个GreetingController:
1 | package com.crossdomain.webapp0.controllers; |
如此启动spring boot之后访问http://localhost:8080/greeting?name=张三
继续添加首页:
在resources目录下创建public目录,添加index.html和hello.js
index.html
1 | <!DOCTYPE html> |
hello.js
1 | $(document).ready(function() { |
将该程序打包放到cluster1.zhangdemo.com和cluster2.zhangdemo.com机器上。
打包命令 mvn clean package
分别运行java -jar web-app0-0.0.1-SNAPSHOT.jar启动web项目
分别访问
http://cluster1.zhangdemo.com/greeting?name=zhangsan
和
http://cluster2.zhangdemo.com/greeting?name=zhangsan
均能正常返回
访问http://cluster1.zhangdemo.com/也能正常有结果:
但是访问http://cluster2.zhangdemo.com/就不行了:
这就是跨域导致的。
查看该页面的请求即发现,有一个请求是从cluster2.zhangdemo.com往cluster1.zhangdemo.com请求的。
解决办法第一种就是在cluster1.zhangdemo.com的nginx配置上添加如下的配置:
1 | location ~ |
Access-Control-Allow-Origin表示授权访问的域,允许任何域可配置成*
刷新http://cluster2.zhangdemo.com/即可正常访问:
从上面的图中可以看到返回头里面添加了
1 | Access-Control-Allow-Origin: http://cluster2.zhangdemo.com |
=====================补充==========================
关于这种方式的跨域配置其实很广泛:
比如支付宝的首页https://my.alipay.com/portal/i.htm ,登录之后的页面加载的资源有很多,重点关注跨域的资源,比如
这里采用的就是简单的服务端的如上面的配置。
但是也有一些并不是这么做的,比如下面的这个资源,这个是获取头像的图片的请求。服务端没有Access-Control-Allow-Origin的响应头返回,但是带上了
1 | Set-Cookie:spanner=eJJ9OVkw6NwuVEZhyKTeGXW6kVdbVTx3Xt2T4qEYgj0=;path=/;secure; |
这样的头,而且请求头也带上了Cookie信息。
另外还发现了另外一类:
请求和响应头没有跨域的痕迹,但是返回的结果是带有callback函数的,这种很明显应该是JSONP实现的。
==================================结束补充==============================
除了这种服务端简单配置的方式,还有另外一种方式可以实现跨域: CORS(Cross-Origin Resource Sharing 跨域资源共享)
下面来介绍一下具体做法。首先cluster2.zhangdemo.com上的程序可以保持不变。
修改在cluster1.zhangdemo.com上部署的程序。具体如下:
1 | @CrossOrigin(origins = "http://cluster2.zhangdemo.com") |
只需要在方法前面加上@CrossOrigin(origins = “http://cluster2.zhangdemo.com“) 跨域的配置即可。
对于@CrossOrigin 注解,这里介绍一下,默认的是允许所有domain的。可以配置的参数有origins, methods, allowedHeaders, exposedHeaders, allowCredentials和maxAge。
另外也可以在class上添加注解。
另外,如果想要做全局的配置的话,添加一个配置类:
1 | @Bean |
重新部署启动cluster1上的程序,可以看到上面的结果,同样添加了两个response的头:
1 | Access-Control-Allow-Origin: http://cluster2.zhangdemo.com |
文章完结。