其实我们通常所说的跨域是狭义的,一枚正直纯

2019-10-05 18:22栏目:美高梅开户送58元官网
TAG:

序言:使用Nginx反向代理,可以解决跨域无权和Session丢失的问题,十分方便。下面我们以前后端分离为案例,展开Nginx的使用教程。

什么是跨域?

大家好,我是IT修真院成都分院第08期学员,一枚正直纯洁善良的web程序员

Nginx下载传送门:Nginx Stable Version Download

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。

今天分享的是Nginx的反向代理跨域

目录

1.背景介绍

2.知识剖析

3.常见问题

4.解决方案

5.编码实战

6.参考文献

7.更多讨论

注意事项:下载之后,记得解压到全英文路径,避免中文路径导致Nginx启动失败。

广义的跨域:

1.背景介绍 

打开nginx.conf ,清空配置项,然后将下面的配置信息原封不动拷贝进去:

1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

 什么是跨域?

跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址, b页面为域名地址,所进行的访问行动都是跨域

2.知识剖析

产生的原因?

浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源

同ip(或domain),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,可以理解为本域脚本只能读写 本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略

现代浏览器在安全性和可用性之间选择了一个平衡点。 在遵循同源策略的基础上,选择性地为同源策略“开放了后门”。例如img script style等标签,都允许垮域引用资源,然而, 你也只能是引用这些资源而已,并不能读取这些资源的内容

同源策略限制以下几种行为:

1.Cookie、LocalStorage 和 IndexDB 无法读取

2.DOM 和 Js对象无法获得

3.AJAX 请求不能发送

常见跨域场景

同一域名,不同文件或路径 允许 同一域名,不同端口 不允许 同一域名,不同协议 不允许 域名和域名对应相同ip 不允许 主域相同,子域不同 不允许 不同域名 不允许

3.常见问题

跨域解决方案

1、 通过jsonp跨域

2、 document.domain + iframe跨域

3、 location.hash + iframe

4、 window.name + iframe跨域

5、 postMessage跨域

6、 跨域资源共享(CORS)

7、 nginx代理跨域

8、 nodejs中间件代理跨域

9、 WebSocket协议跨域

4.解决方案

nginx反向代理解决跨域

正向代理:代理位于网站和客户端中间, 客户端无法访问某网站,就将请求发送给代理服务器,代理从网站取回来再发送给客户端,网站并不知道为谁提供服务

反向代理:客户端访问某网站的一个页面, 但是网站并没有,就偷偷从另外一台服务器上取回来,然后作为自己的内容吐给用户,用户不知道真正提供服务的是谁

对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过 检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确 处理请求,并且并不知道这个请求是来自代理服务器的。

简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又通过重写url,欺骗了真实 的服务器,让它以为这个http请求是直接来自与用户浏览器的。

Location/carrots-admin-ajax/{

proxy_pass;

}

proxy_pass把请求代理到其他主机

两种写法h

如果访问url =http://server/html/test.jsp,则被nginx代理后

针对情况1,将test/作为根路径,请求test/路径下的资源。

针对情况2,则被nginx代理后,请求路径会变为

/carrots-admin-ajax/

是一个匹配规则,用于拦截请求,匹配任何以/proxy/html/开头的地址,匹配符合以后,停止往下搜索正则。

对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确处理请求,并且并不知道这个请求是来自代理服务器的。

简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又通过重写url,欺骗了真实的服务器,让它以为这个http请求是直接来自与用户浏览器的。

nginx rewrite指令执行顺序:

1.执行server块的rewrite指令(这里的块指的是server关键字后{}包围的区域,其它xx块类似)

2.执行location匹配

3.执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件

如果循环超过10次,则返回500 Internal Server Error错误

7.参考文献

参考一:https://www.cnblogs.com/gabrielchen/p/5066120.html

参考二:http://blog.csdn.net/shendl/article/details/48443299

8.更多讨论

提问:

 Q:  除了nginx,还有哪些跨域方法

A:1、 通过jsonp跨域

2、 document.domain + iframe跨域

3、 location.hash + iframe

4、 window.name + iframe跨域

5、 postMessage跨域

6、 跨域资源共享(CORS)

7、 nginx代理跨域

8、 nodejs中间件代理跨域

9、 WebSocket协议跨域

Q:例如img script style等标签,都允许垮域引用资源?

A:在浏览器中,并且加载的方式其实相当于一次普通的GET请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。

Q:例如img script style等标签,都允许垮域引用资源?

A:在浏览器中,并且加载的方式其实相当于一次普通的GET请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。

Q:JSONP和nginx跨域有什么不同

JSONP和nginx是完全不同的 是可以跨域的,而且在跨域脚本中可以直接回调当前脚本的函数

原理:是可以跨域的,而且在跨域脚本中可以直接回调当前脚本的函数

script标签是可以加载异域的JavaScript并执行的,通过预先设定好的callback函数来实现和母页面的交互。它有一个大名,叫做JSONP跨域,JSONP是JSON with Padding的略称。它是一个非官方的协议,明明是加载script,为啥和JSON扯上关系呢?原来就是这个callback函数,对它的使用有一个典型的方式,就是通过JSON来传参,即将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。JSONP只支持GET请求。

worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #前端页面服务器 server { #监听端口和域名 listen 7000; server_name localhost; #添加头部信息 proxy_set_header Cookie $http_cookie; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #添加拦截路径和代理地址 location /api/ { proxy_pass http://localhost:8080/; #注意:使用代理地址时末尾记得加上斜杠"/"。 } #添加拦截路径和根目录 location / { root html/hehe; #注意:使用"/"拦截全路径的时候记得放在最后。 index index.html index.htm; #index表示首页 } }}

其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

在Windows 环境中:

什么是同源策略?

  • 快速启动Nginx:右键管理员模式,运行nginx.exe。

  • 快速关闭Nginx:在nginx主目录,添加关闭Nginx的命令。

    其中结束Nginx.bat的具体内容如下:

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

同源策略限制以下几种行为:

taskkill /f /im nginx.exe
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送

图片 1使用命令快速关闭Nginx

常见跨域场景

前后端分离后,可以直接将静态资源部署到Nginx的html目录。这里我们在$nginx_home/html目录下创建一个名为hehe的文件夹,并添加一个页面(index.html)用于跨域访问测试,index页面内容如下:

URL                   说明          是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js     同一域名,不同文件或路径      允许
http://www.domain.com/lab/c.js
http://www.domain.com:8000/a.js
http://www.domain.com/b.js     同一域名,不同端口        不允许
http://www.domain.com/a.js
https://www.domain.com/b.js    同一域名,不同协议        不允许
http://www.domain.com/a.js
http://192.168.4.12/b.js      域名和域名对应相同ip       不允许
http://www.domain.com/a.js
http://x.domain.com/b.js      主域相同,子域不同        不允许
http://domain.com/c.js
http://www.domain1.com/a.js
http://www.domain2.com/b.js    不同域名             不允许
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"/> <title>Page Index</title></head><body><h2>前台系统7000</h2><p ></p><p ></p></body><script src="js/jquery.js"></script><script> $.ajax({ url: 'http://localhost:7000/api/user/login/verifyCode', type: "POST", success: function  { //1.获取验证码 $.html("跨域访问成功:verifyCode:" + data); //2.核对验证码 $.ajax({ url: 'http://localhost:7000/api/user/login/checkVerifyCode', type: "POST", success: function  { $.html("跨域访问成功:checkVerifyCode:" + data); } }); }, error: function  { $.html; } });</script></html>

跨域解决方案

首先在POM文件添加Web依赖,然后编写控制层,提供对外的访问接口。默认启动端口是8080.

1、 通过jsonp跨域

package com.hehe;@SpringBootApplication@RestController@RequestMapping("/user/login/*")public class SpringBootNginxApplication { //在拦截器打印访问URL @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptor() { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { if(response.getStatus()/100>=4){ System.err.println("访问URL:"+request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE)); }else { System.out.println("访问URL:"+request.getRequestURI; } } }); } }; } //提供验证码 @RequestMapping("verifyCode") public String verifyCode(HttpServletRequest request) { request.getSession().setAttribute("verifyCode", "N7GX"); return request.getSession + ":" + request.getSession().getAttribute("verifyCode"); } //核对验证码 @RequestMapping("checkVerifyCode") public String checkVerifyCode(HttpServletRequest request) { return request.getSession + ":" + request.getSession().getAttribute("verifyCode"); } public static void main(String[] args) { SpringApplication.run(SpringBootNginxApplication.class, args); }}

2、 document.domain + iframe跨域

打开浏览器,访问 ,可以看到后台获取的Session和第一次生成的验证码。如图:

3、 location.hash + iframe

图片 2

4、 window.name + iframe跨域

打开浏览器,访问 ,可以看到后台获取的Session和第二次取出的验证码。

5、 postMessage跨域

图片 3

6、 跨域资源共享(CORS)

由上图可以看到,跨域访问成功,并且Session没有丢失。

7、 nginx代理跨域

简单来说,Nginx是间接跨域,而CORS则实现了直接跨域。Nginx的反向代理“欺诈了”浏览器,所以浏览器和服务器都认为是同源访问,所以Session不会丢失。(PS:如果发生跨域访问,服务器会每次都创建新的Session,所以才造成了前后端分离的Session丢失问题。) 至于CORS这种跨域机制的安全性和灵活性更高,但需要自己解决跨域访问Session丢失的问题,通常情况可以采用Session+Redis来实现Session共享。)

8、 nodejs中间件代理跨域

第1步:

9、 WebSocket协议跨域

第1步是打开页面,第2步是在这个页面发起AJAX请求,并且请求的域名端口均与当前访问页面相同,属于同源操作,所以浏览器不会报出跨域禁止的错误。

一、 通过jsonp跨域

第3步:

通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

第3步是本案例最为关键的一步,真正的跨域操作由Nginx的proxy_pass进行完成,并成功将验证码信息以代理的身份返回给浏览器,让浏览器处于同源访问后台的错觉。打开F12可以看到代理服务器:

1.)原生实现:

图片 4image.png

 <script>
  var script = document.createElement('script');
  script.type = 'text/javascript';
  // 传参并指定回调执行函数为onBack
  script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
  document.head.appendChild(script);
  // 回调执行函数
  function onBack(res) {
    alert(JSON.stringify(res));
  }
 </script>

通常情况下,建议大家在代理地址末尾加上"/" ,表示转发后就是proxy_pass直接拼接映射路径。

服务端返回如下(返回时即执行全局函数):

Nginx代理效果:转发前URL:

onBack({"status": true, "user": "admin"})
 server { listen 7000; server_name localhost; #正确示范: 末尾加斜杠"/" location /api/ { proxy_pass http://localhost:8080/hehe/; } }

2.)jquery ajax:

如果代理地址末尾没有加斜杠的话,表示转发后也是proxy_pass直接拼接映射路径,但是,拼接的时候会少了个"/",这样会引发访问路径错误。

$.ajax({
  url: 'http://www.domain2.com:8080/login',
  type: 'get',
  dataType: 'jsonp', // 请求方式为jsonp
  jsonpCallback: "onBack",  // 自定义回调函数名
  data: {}
});

Nginx代理效果:转发前URL:

3.)vue.js:

 server { listen 7000; server_name localhost; #错误示范: 末尾无斜杠 location /api/ { proxy_pass http://localhost:8080/hehe; } }
this.$http.jsonp('http://www.domain2.com:8080/login', {
  params: {},
  jsonp: 'onBack'
}).then((res) => {
  console.log(res); 
})

为了更方便大家,看到在Nginx调整了proxy_pass有无斜杠的区别,楼主在控制台打印了每次请求访问的URL地址,这样更加清晰:

后端node.js代码示例:

具体做法:关闭Nginx 将代理地址修改为:proxy_pass ,然后启动Nginx,在浏览器访问

var querystring = require('querystring');
var http = require('http');
var server = http.createServer();
server.on('request', function(req, res) {
  var params = qs.parse(req.url.split('?')[1]);
  var fn = params.callback;
  // jsonp返回设置
  res.writeHead(200, { 'Content-Type': 'text/javascript' });
  res.write(fn + '(' + JSON.stringify(params) + ')');
  res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');

图片 5

jsonp缺点:只能实现get一种请求。

源码地址:SpringBoot+Nginx (PS:内附本案例使用的完整Nginx )

二、 document.domain + iframe跨域此方案仅限主域相同,子域不同的跨域应用场景。

专题阅读:《SpringBoot 布道系列》

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

原文链接: 转载请注明出处

1.)父窗口:()

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
  document.domain = 'domain.com';
  var user = 'admin';
</script>

2.)子窗口:()

<script>
  document.domain = 'domain.com';
  // 获取父窗口中变量
  alert('get js data from parent ---> ' + window.parent.user);
</script>

三、 location.hash + iframe跨域

实现原理: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

版权声明:本文由美高梅开户送58元官网发布于美高梅开户送58元官网,转载请注明出处:其实我们通常所说的跨域是狭义的,一枚正直纯