前后端分离架构下的跨域问题

时间:2022-02-14 19:41:29

来源:
http://mp.weixin.qq.com/s?__biz=MjM5NTM1NDcyOQ==&mid=202557064&idx=1&sn=d24349248e5dd70e0d0bcdc0fb6e6ca5#rd

前后端分离架构下的跨域问题

同源策略与跨域

所谓跨域,英文叫做cross-domain,是网络安全领域的一个专有名词。简单点理解就是某些操作越过了域名的界限,访问了别的域名。

如果脚本可以*访问其他域,就会产生很多安全问题。

比如,假设有一个网上银行系统,你已经登录过了,它支持一个ajax api可以进行转账;有一个论坛系统,人气很高,但是其中有恶意脚本,这个脚本会调用这个ajax api,从当前登录的用户账户中,转1000块到攻击者的账户。这样,当你访问这个论坛的时候,就会被转走1000块,而你一点都不知道!

除此之外,跨域请求还有很多危害。这不是一本关于安全的书,也就不展开讲了,想深入了解的可以买一本余弦编写的《Web前端黑客技术揭秘》。

为了防范跨域攻击,所有现代浏览器都遵循一套同源策略。根据MDN上的定义,“如果两个页面拥有相同的协议(protocol),端口(如果指定),和主机,那么这两个页面就属于同一个源(origin)”。对于违反同源策略的请求,除了img src等少数嵌入操作之外,都会被浏览器阻止。

这里需要注意的是:同源不仅仅要求相同的域名或ip,连协议和端口也必须相同。

比如https://localhosthttp://localhosthttp://localhost:3000就不是同源的,而http://localhost/apihttp://localhost/views是同源的。

我们平常所说的“跨域”其实就是指“发起不同源请求”,而这样的跨域请求会被浏览器阻止。

如何解决跨域问题?

JSONP方式

最初用来解决跨域问题的方式,叫做JSONP,它的基本原理是:跨域的“资源嵌入”是被浏览器允许的。所以,可以通过一个script标签来嵌入一段来自其他服务器的脚本。由于这个脚本完全运行在当前域,无法访问第三方服务器的cookie等敏感信息,所以是安全的。

JSONP的缺点是它只能支持GET操作,没法支持POST等操作,但是由于兼容性好等优点,仍然有很多网站采用JSONP的方式公开自己的API供第三方调用。

在Angular中,$http内置了对JSONP的支持,它的调用接口也和其他方法没什么区别,使用起来非常简单。

反向代理方式

要想解决跨域问题,最简单彻底的方法当然是把他们拉到一个域下,而这就是该“反向代理”发挥作用的时候了。

所谓反向代理,就是在自己的域名下架设一个Web服务器,这个服务器会把请求转发给第三方服务器,然后把结果返回给客户端。

这时候,在客户端看来,自己就是在和这台反向代理服务器打交道,而不知道第三方服务器的存在。

所以,如果有一个Web服务程序,它同时提供了反向代理功能和静态文件服务功能,静态文件服务负责渲染前端页面,反向代理则提供对第三方服务器的透明访问。那么前端和后端就变成了同源的,不再受同源策略的约束。

那么,有这样的Web服务程序吗?有,而且不止一个。事实上,几乎所有的主流Web服务器都提供了反向代理功能。这里仅以nginx为例来示范反向代理的配置方式,其他Web服务器请搜相应的文档自行研究。


server {

listen 80;

server_name your.domain.name;

location / {

proxy_pass http://localhost:5000/; # 把根路径下的请求转发给前端工具链(如gulp)打开的开发服务器,如果是产品环境,则使用root等指令配置为静态文件服务器

}

location /api/ {

proxy_pass http://localhost:8080/service/; # 吧 /api 路径下的请求转发给真正的后端服务器

proxy_set_header Host $http_host; # 把host头传过去,后端服务程序将收到your.domain.name,否则收到的是localhost:8080

proxy_cookie_path /api /service; # 把cookie中的path部分从/api替换成/service

proxy_cookie_domain localhost:8080 your.domain.name; # 把cookie的path部分从localhost:8080替换成your.domain.name

}
}

注意最后这两句话,由于cookie中存在一个path机制,可以对同一个域下的不同子域进行区分。所以,如果后端所使用的路径是/service,而前端使用的路径是/api,那么前端将不能访问后端的cookie,这就导致登录等操作所写入的cookie无法正常传入传出,其表现则是登录始终没有效果。cookie的domain机制也是类似的原理。

现实中的后端服务器,使用path机制的很多,所以这项设置非常实用。

CORS方式

这是W3C提供的另一种跨域方式。作为一项标准的跨域规范,CORS本应该是最值得采用的。

CORS全称是”跨域资源共享”(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

问题在于,老式浏览器不支持CORS,而我们显然还没到可以无视老式浏览器的时候。 所以,只要有可能,就应该优先采用反向代理的方式。

CORS的原理是基于服务方授权的模式,也就是说提供服务的程序要主动通过CORS回应头来声明自己信任哪些源(协议+域名+端口)。 由于得到了服务方的授权,浏览器就可以放行来自这些域的请求了。