一、
首先同源策略是什么:
同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互 但在实际开发中,我们常常需要进行跨域访问
源的定义:
如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。这个方案也被称为“协议/主机/端口元组”,或者直接是“元组”。(“元组”是指一组项目构成的整体,具有双重/三重/四重/五重等通用形式。’)
URL | 结果 | 原因 |
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同 http:默认端口是 80 |
http://news.company.com/dir/other.html | 失败 | 主机不同 |
源的继承:
在页面中通过 about:blank
或 javascript:
URL 执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URL 没有包含源服务器的相关信息。
跨源网络访问
同源策略控制不同源之间的交互,例如在使用 XMLHttpRequest 或 <img> 标签时则会受到同源策略的约束。这些交互通常分为三类:
- 跨源写操作(Cross-origin writes)一般是被允许的。例如链接、重定向以及表单提交。特定少数的 HTTP 请求需要添加预检请求。
- 跨源资源嵌入(Cross-origin embedding)一般是被允许的(后面会举例说明)。
- 跨源读操作(Cross-origin reads)一般是不被允许的,但常可以通过内嵌资源来巧妙的进行读取访问。例如,你可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或得知内嵌资源的可用性
如何允许跨源访问
可以使用 CORS 来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。
如何阻止跨源访问
- 阻止跨源写操作,只要检测请求中的一个不可推测的令牌(CSRF token)即可,这个标记被称为跨站请求伪造(CSRF)令牌。你必须使用这个令牌来阻止页面的跨源读操作。
- 阻止资源的跨源读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。
- 阻止跨源嵌入,需要确保你的资源不能通过以上列出的可嵌入资源格式使用。浏览器可能不会遵守
Content-Type
头部定义的类型。例如,如果你在 HTML 文档中指定<script>
标记,则浏览器将尝试将标签内部的 HTML 解析为 JavaScript。当资源不是网站的入口点时,还可以使用 CSRF 令牌来防止嵌入。
跨源数据存储访问
访问存储在浏览器中的数据,如 Web Storage 和 IndexedDB,是以源进行分割的。每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对属于其他源的数据进行读写操作。
Cookie 使用不同的源定义方式。一个页面可以为本域和其父域设置 cookie,只要是父域不是公共后缀(public suffix)即可。Firefox 和 Chrome 使用 Public Suffix List 检测一个域是否是公共后缀。当你设置 cookie 时,你可以使用 Domain
、Path
、Secure
和 HttpOnly
标记来限定可访问性。当你读取 cookie 时,你无法知道它是在哪里被设置的。即使只使用安全的 https 连接,你所看到的任何 cookie 都有可能是使用不安全的连接进行设置的
二、
什么是跨域?
跨域指的是两个 URL 的协议、域名、端口只要其中一个不一致,就会形成跨域。
出现跨域的根本原因 浏览器的同源策略不允许非同源的 URL 之间进行资源的交互
ajax跨域请求解决方案:
-
jsonp跨域:
jsonp (JSON with Padding),是JSON的一种“使用模式”,可以让网页跨域读取数据。其本质是利用script标签的开放策略,浏览器传递callback参数到后端,后端返回数据时会将callback参数作为函数名来包裹数据,从而浏览器就可以跨域请求数据并定制函数来自动处理返回数据。
看图:
示例代码:
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参callback给后端,后端返回时执行这个在前端定义的回调函数
script.src = 'http://a.qq.com/index.php?callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
jsonp跨域优点:
-
jsonp兼容性强,适用于所有浏览器,尤其是IE10及以下浏览器
jsonp跨域缺点:
-
没有关于调用错误的处理。
-
只支持GET请求,不支持POST请求以及大数据量的请求,而且也无法拿到相关的返回头,状态码等数据。
-
callback参数恶意注入,可能会造成xss漏洞。
-
无法设置资源访问授权。
跨域资源共享(CSRS)
跨域资源共享(Cross-origin resource sharing,CORS)是一个 W3C标准,允许浏览器向跨域服务器发送请求,从而克服了ajax只能同源使用的限制
实现CORS通信的关键是服务器,需要服务器配置Access-Control-Allow-Origin头信息。当CORS请求需要携带cookie时,需要服务端配置Access-Control-Allow-Credentials头信息,前端也需要设置withCredentials
CORS请求分为两种:一种是简单请求和非简单请求
请求方法是以下三种方法之一:HEAD、GET、POST
HTTP的请求头信息不能超出:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 等以上这几个字段。
如下思维导图:
看示例代码:
// IE8/9需用XDomainRequest兼容
var xhr = new XMLHttpRequest();
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://a.qq.com/index.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=saramliu');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
CORS跨域优点:
-
支持所有类型的HTTP请求,功能完善。
-
通过onerror事件监听进行调用错误处理;
通过Access-Control-Allow-Origin进行资源访问授权。
CORS跨域缺点:
-
目前主流浏览器(IE10及以上)都支持CORS,但IE8和IE9需要使用XDomainRequest对象进行兼容,IE7及以下浏览器不支持
配置Filter
我们还可以通过配置过滤器来解决跨域问题,这种方式更灵活,可以满足复杂的跨域需求:
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "http://example.com");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
}
@Component:这个注解表示该类是一个Spring组件,会被Spring容器管理。
Filter:这是一个Servlet过滤器接口,用于在请求和响应之间进行过滤处理。
doFilter:这是过滤器的核心方法,每次请求都会执行这个方法。
ServletRequest和ServletResponse:表示HTTP请求和响应。
FilterChain:用于传递请求和响应到下一个过滤器或目标资源。
HttpServletResponse res = (HttpServletResponse) response:将ServletResponse强制转换为HttpServletResponse,以便设置CORS头。
setHeader方法:设置CORS相关的HTTP头,允许指定域、方法和是否发送Cookie。
代理服务器:
没有代理,浏览器通过http://localhost:8080/home.htm
l获取html
网页数据,在html
页面向http://localhost:9000/xx
获取html
页面具体数据,由于浏览器同源策略请求是不允许的
通过nginx
或Node.js
代理后,获取url
为http//localhost:9000/xxx.html
的html
页面
获取url
为http://localhost:9000/api/xxx
页面内的数据 ,相同的http://localhost:9000
并不会引发跨域问题,只不过带api
前缀的被Node.js
或ngiinx
代理了,向http://localhost:8080/xxx
获取对应资源后再返回给客户端
-
实际解决问题(在前端添加代理配置)
在vue.config.js
中添加如下配置,只要是带api
前缀的都会被代理到http://localhost:9000
,请求路径http://localhost:8080/api/xxx
被代理到http://localhost:9000/api/xxxx
这样解决了跨域问题问题module.exports = { devServer:{ proxy:{ '/api':{//匹配所有以'/api'开头的请求路径 target:'http://localhost:9000',//代理目标的基础路径 changeOrigin:true, //可要可不要看具体需求 pathRewrite:{'^/api':''} 可要可不要看具体需求 } } } }
希望能帮到你们