写点什么

重学 JS | 跨域的原因和解决方案

用户头像
梁龙先森
关注
发布于: 2021 年 01 月 19 日
重学JS | 跨域的原因和解决方案

了解跨域之前,我们先熟悉下浏览器同源策略。

浏览器同源策略

同源策略,即为相同协议、域名和端口号,若存在任何一点不同,而为非同源。它是浏览器最基本也是最核心的安全功能,约定客户端脚本在没有明确授权情况下,不能读写不同源的目标资源。主要存在 2 种表现形式:

  1. DOM 同源测试

禁止对不同页面进行 dom 操作,主要场景是 iframe 跨域,不同域名下的 iframe 是会限制访问的。

  1. XMLHttpRequest

禁止使用 XMLHttpRequest 向不同源的服务器发送 ajax 请求,防止跨站请求伪造 CSRF。

浏览器跨域解决

当没有遵守浏览器的同源策略的时候,会导致跨域限制,很大程度上这也保护了用户隐私数据的安全。但是在某些业务场景中,不可避免的需要进行跨域访问,这怎么解决呢?

1. CORS

CORS 全称“ 跨域资源共享”,是 HTML5 支持的协议,允许浏览器向跨源服务器发起 XMLHttpRequest 请求。它通过服务器增加特殊的 Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持 CORS、并且判断 Origin 通过的话,则允许 XMLHTTPRequest 发起跨域请求。这就需要服务端通过对响应头的设置,接收跨域请求处理。这里以 java 为例配置跨域:

  1. 配置全局过滤器

@Configurationpublic class GlobalCorsConfig {    @Bean    public CorsFilter corsFilter() {        CorsConfiguration config = new CorsConfiguration();          config.addAllowedOrigin("*");          config.setAllowCredentials(true);          config.addAllowedMethod("*");          config.addAllowedHeader("*");          config.addExposedHeader("*");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource); }}
复制代码
  1. 配置全局拦截器

@Configurationpublic class MyConfiguration extends WebMvcConfigurerAdapter  {
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedHeaders("*") .allowedOrigins("*") .allowedMethods("*");
} }
复制代码
  1. 单个请求的跨域通过

@RequestMapping("/hello")@CrossOrigin("http://localhost:8080") public String hello( ){	return "Hello World";}
复制代码


CORS 把 Http 请求分为两类,不同类别按不同的策略进行跨域资源共享协商。

  1. 简单的跨域请求

  2. 请求方法是 GET、HEAD 或者 POST,并且当请求方法是 POST 时,Content-Type 必须是 application/x-www-form-urlencoded, multipart/form-data 或着 text/plain 中的一个值。

  3. 请求中没有自定义 HTTP 头部。

浏览器要做的就是在 HTTP 请求中添加 Origin Header,将 JavaScript 脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加 Access-Control-Allow-Origin Header。浏览器收到响应后,查看 Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给 JavaScript。否则浏览器忽略此次响应。


  1. 带预检(preflighted)的跨域请求

  2. 除 GET、HEAD 和 POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他 HTTP 方法。

  3. 请求中出现自定义 HTTP 头部。

带预检(Preflighted)的跨域请求需要浏览器在发送真实 HTTP 请求之前先发送一个 OPTIONS 的预检请求,检测服务器端是否支持真实请求进行跨域资源访问,真实请求的信息在 OPTIONS 请求中通过 Access-Control-Request-Method Header 和 Access-Control-Request-Headers Header 描述,此外与简单跨域请求一样,浏览器也会添加 Origin Header。服务器端接到预检请求后,根据资源权限配置,在响应头中放入 Access-Control-Allow-Origin Header、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers Header,分别表示允许跨域资源请求的域、请求方法和请求头。此外,服务器端还可以加入 Access-Control-Max-Age Header,允许浏览器在指定时间内,无需再发送预检请求进行协商,直接用本次协商结果即可。浏览器根据 OPTIONS 请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。这个过程对真实请求的调用者来说是透明的。


XMLHttpRequest 支持通过 withCredentials 属性实现在跨域请求携带身份信息(Credential,例如 Cookie 或者 HTTP 认证信息)。浏览器将携带 Cookie Header 的请求发送到服务器端后,如果服务器没有响应 Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。

2. JSOP

JSONP 是利用 script 标签的 src 属性不受同源策略约束来跨域获取数据的,它实现跨域的特点是简单适用、兼容老浏览器、对服务端影响小。缺点是需要后端配置输出特定的返回信息,并且只支持 Get 请求,并且回调函数必须是全局的,因为服务端返回后会从全局环境开始找回调函数。

实现步骤分为 2 步:

  1. 在网页中动态创建 script 标签,通过 script 标签向服务器发起请求,在请求中携带一个请求的 callback 回调函数。

var callback = function(){  // 回调函数 successFun	var url = `localhost:8080/getUser?callback=successFun`  var script = document.createElement('script')  script.src = url  document.body.appendChild(script)}
复制代码
  1. 在服务器接收到请求后,会处理响应获取返回的参数,然后将参数放在 callback 回调函数中对应的位置,并将 callback 回调函数通过 json 格式进行返回。

// 示意例子,不严谨@RequestMapping("getUser")@ResponseBodypublic String getUser(String callback){    User user = new User();    user.setName("测试测试");    String str = JSONObject.toJSONString(user);    //jsonp处理    return callback + "(" + str + ")";}
复制代码


总结,自此我们完成了对跨域问题相关知识的学习。


发布于: 2021 年 01 月 19 日阅读数: 26
用户头像

梁龙先森

关注

脚踏V8引擎的无情写作机器 2018.03.17 加入

还未添加个人简介

评论

发布
暂无评论
重学JS | 跨域的原因和解决方案