欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

跨域请求的原因及解决方案

程序员文章站 2022-04-15 12:34:16
...

一、什么是跨域?

请求方与服务方的源不同,就是跨域,包括:

  • 协议不同
  • 域名不同
  • 端口不同

跨域的缘由:
主要是因为浏览器指定的同源策略,即“协议+域名+端口”都相同,它是浏览器最基本和最核心的安全机制,关闭它会容易受到XSS和CSRF的攻击。

跨域的后果:

  1. Cookie,LocalStorage,IndexDB无法获取
  2. DOM和JS对象无法获得
  3. AJAX请求不能发送(实际能发送,但拿不到结果,被拦截下来)

二、跨域解决方案

1、Jsonp请求:
jsonp由两部分组成:回调函数和数据。回调函数是当浏览器收到响应时调用的函数,回调函数名一般在请求种指定,数据就是服务端处理完请求返回的数据,作为回调函数的参数。如:

http://somewhere-else/json/?callback=handleResponse

实现原理: 通过动态创建一个script,通过这个script去请求,请求完,该script即被销毁。可通过对JQ打断点的方式验证(可看见JQ在网页源码嵌入了一个临时的script,当Jsonp请求完成之后,该script即被销毁。)

缺点:

  1. 只能支持GET类型的请求,不支持POST, PUT, DELETE请求。
  2. Jsonp是从其他域种加载代码执行,可能会夹杂一些恶意代码,所以在使用jsonp时一定要保证被请求方安全可靠。
  3. jsonp请求返回的不是json数据,而是一个js脚本,需要后台服务器配合封装。

2、CORS:跨域资源共享,定义了在跨域访问时,浏览器和服务器的沟通方式,使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定跨域请求或响应时应该成功还是失败。

如:发起一个GET跨域请求,浏览器会为http头部加上一个额外的Origin头部,其中包含了页面的源信息(协议、域名和端口号),这个额外的Origin决定了服务器是否响应该请求。一个Origin头部实例:

Origin: https://www.somewhere-else.net

如果服务器认可该请求就会在响应头加上Access-Control-Allow-Origin标志字段,值可以是与请求头带来的Origin相同,如果该服务器上的是公共资源,值就是“*”。

Access-Control-Allow-Origin: https://www.somewhere-else.net

如果响应头没有这个字段,说明服务器拒绝了这次跨域请求,会抛出一个错误。
但是并不能被xhr的onerror事件捕获。默认情况下跨域请求都是不带凭证的(cookie,HTTP认证及服务端SSL证明等),通过修改xhr对象的withCredentials(IE10以前的版本不支持该属性)设置为true,可以指定某个请求携带凭证。如果服务器允许跨域请求携带凭证响应头部会有标示。

Access-Control-Allow-Credentials: true

如果发送的是带凭证的请求,响应头里却没有这个字段,那么浏览器就不会吧响应交给JS,意思是xhr获取到的responseText为空,status为0,这个时候onerror可以捕获到该错误。

通过@CrossOrigin注解来实现跨域请求:纯粹Java后台实现,前端照常使用Ajax请求。

@RequestMapping("/loadData")
@ResponseBody
@CrossOrigin
public Object loadData() {
   String msg = "Hello JSONP";
   return msg;
}

也可以添加参数,进一步限制允许跨域请求的范围:

@RequestMapping("/loadData")
@ResponseBody
@CrossOrigin(origins = "http://localhost:8080", methods = {RequestMethod.POST, RequestMethod.GET})
public Object loadData() {
   String msg = "Hello JSONP";
   return msg;
}

@CrossOrigin不仅支持在方法上添加,还支持在Controller类上添加,代表该Controller下面所有方法都支持跨域请求。

实际上底层的实现原理就是通过添加以下响应头实现的:
resp.setHeader("Access-Control-Allow-Origin", "*")
当然这种是所有的跨域请求都放行,不是很安全,如果你需要只让某些网络访问,那么可以将*替换为具体的请求。
相关标签: 跨域请求