前言:在以往开发中,不会涉及到跨域的问题,因为往往都是在同一个项目中开发代码或者单纯写小demo;However,在实际项目中,前后端分成两个不同的项目,各自部署在不同的域名下,这也就会遇到跨域问题了。
![](https://img.haomeiwen.com/i13908708/17d15829e7eb6f2c.png)
既然问题发生了,那就要从根本上去解决问题,在开始说解决方案前,我们有必要了解一下什么是跨域
1、什么是跨域
在浏览器同源策略限制下,向不同源(不同协议、不同域名或者不同端口)发送XHR请求,浏览器认为该请求不受信任,禁止请求,具体表现为请求后不正常响应
从定义中可以看到这都是浏览器做的“好事”。那么什么是不用源呢?其实也很简单,只要页面的协议、域名和端口与请求地址的没有完全一样,就认为你是不同源,举个极端例子来说:
http://www.baidu.com:80
与
https://msg.baidu.com:8080
2、怎么解决跨域
只要思想不滑坡,办法总比困难多,既然已经了解什么是跨域,那我们就着手来解决这个问题
1.从浏览器入手
其实跨域只在浏览器下才会触发,那有没有办法禁用浏览器的同源策略呢?办法还真的有。
对于IE浏览器:进入ie的网际网路选项设置,然后选择安全性,再选择自订等级,然后下拉,找到「存取跨网络的资料来源」,选择启用即可;
对于chrome浏览器:通过在命令行,输入chrome.exe --allow-file-access-from-files --user-data-dir="C:/Chrome dev session" --disable-web-security,这会新建一个浏览器实例,自动打开的chrome会显示一系列黄色的文字就说明成功了;
对于FireFox浏览器:在地址栏输入about:config,然后下拉找到security.fileuri.strict_origin_policy,然后设置为false即可
2.从域名入手
既然域名等信息不一致导致跨域产生,那么干脆就把两个项目合并成一个项目,使用相同的协议、域名和端口。
3.从jsonp入手
其实认真想想,我们的系统中经常会用到外链的图片、样式文件以及插件,那这些不会导致跨域吗?是的,真的不会,因为这些是http请求,并不是前面定义的xhr(XmlHttpRequest)请求。
既然图片和js脚本可以正常请求,那么如果把script脚本的src改成我需要跨域请求的url是不是就可以了呢?
是可以的,当请求接口返回的数据需要稍微处理下。在平常引入script脚本时,下载完文件后自动执行,如果我们把src改成跨域url,而且返回值是一段jjs语句,比如:
req.send("alert(1)"); //后台返回js语句
那么前台会会自动解析并执行(这里是弹出1)。那么,如果返回的js语句是一个调用函数的js语句,恰巧前台事先定义了该函数,如:
//前台
function fun(msg){ alert(msg); }
//后台
req.send("fun('你渴望力量吗')");
那么在fun函数将被调用(可以理解成后台调用前台代码),如果对fun的参数进行处理,就可以实现复杂的业务逻辑了。
在实际情况下是怎么处理的呢?一般我们会先协商好需要被调用的方法及参数(fun),然后动态创建一个script标签,并设置该标签的src为跨域url,最后插入到文档中,插入后,浏览器会自动发起http的get请求,下载完成后将会执行后台返回的js语句(后台调用前台)。
这就是jsonp。
4.从代理入手
还是回到最开始,既然浏览器认为非同源不安全,那么向同源请求不就行了?我们把请求转到同一项目下的后台,在同项目的后台进行xhr请求,然后把请求结果原样返回给前台,这就是代理(或者叫转发)。
成功的原因是同源策略只在浏览器下才起作用,我用后台来请求其他的url,那是不受影响的。开启后台代理可以用C#、JAVA、NodeJs、甚至是python都行,只要能被前端访问的并且能转发请求就可以。
5.从CORS入手
除了前面的几种方法外,还有一种简单得多的方法,那就是后台的请求响应头告诉浏览器“我的这个请求很安全,允许当前域名跨域访问”。如何去实现的呢,其实这是利用了CORS(Cross-Origin Resource Sharing, 跨源资源共享),听起来牛逼哄哄的,其实也就是一个W3C新标准,浏览器检测到响应头的一些字段的值后,跳过同源策略。
那么有那些响应头字段,以及它们分别是什么定义呢?
res.header("Access-Control-Allow-Origin", "*"); //允许全部域名跨域,可以指定特点域名,逗号分隔
res.header("Access-Control-Allow-Credentials", "true"); //允许携带cookie
res.header("Access-Control-Allow-Headers", "X-Requested-With"); //允许传输的请求头
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //允许发送的xhr模式
res.header("X-Powered-By", " 3.2.1"); //快速模式
res.header("Content-Type", "application/json;charset=utf-8"); //类型及字符编码
3、如何实现跨域下的登陆
用前面任意一种方案都可以解决跨域,然而,跨域带来的登陆问题却不好解决。在说如何解决登陆问题前,还是按照老规则说一下问什么就登陆不了了。
在解析前需要先明确几点:
1:http是无状态的,不知道该请求归属于谁
2:每次http请求都会自动携带cookie(在请求头中)
3:session存放在服务端,是有时间有效性的,一段时间内不访问将失效
在正常情况下(非跨域),我们前台发出post请求,携带账号、密码、验证码,提交给后台,后台检查数据后,设置session并返回登陆信息完成登陆,这就是一个简单的登录流程。当用户发起一个新的请求的时候,后台通过请求上下文可以获取到已经保存在服务端的session信息,如:
req.session["UserInfo"]
//登录时设置了key为UserInfo的session
乍一看就是这么一回事,当时仔细想想又会发现奇怪的地方:
①都说了http是无状态的,在上一个请求设置的session,为什么在下一个请求中可以获取到呢?
②session的key是UserInfo,如果有多个用户同时操作,拿到的值会不会是同一个呢?
接下来我们先介绍一下session的设置与读取
说起session,就不得不说cookie,两者都是缓存,只不过前者保存在服务端,后者保存在客户端。在登录完成后,后台设置一个key为UserInfo的session对象(就叫对象S吧),这个对象S有三个重要的属性,分别是key、value以及sid。key和value的非常好理解,那sid是拿来干嘛?
其实sid又叫session_id,是这个session对象的主键。
众所周知,http是无状态的,为了区分状态,在设置session的时候,服务端会自动在http请求的响应头中把sid设置到cookie中(开发人员感知不到这步操作的,也就是自动设置cookie),浏览器响应请求后,会把更新带有sid的cookie,并在下一个请求时自动带上cookie(浏览器请求自动携带cookie,也是静默操作,关于携带cookie的后面还会继续介绍)。
当第二次请求来到后台,此时已经带上了sid的cookie,如果这时候获取session,服务器将会遍历session列表,匹配所有sid是cookies中的sid的session,再通过key作为索引找到相应的结果。
这就是设置session和读取session的流程,下面是一个简单的图片说明
![](https://img.haomeiwen.com/i13908708/9931b7b3f8affeb1.jpg)
![](https://img.haomeiwen.com/i13908708/5020c9a0b38107c7.png)
网友评论