前端ajax跨域问题分析

产生Ajax跨域的三要素

  • 浏览器限制:浏览器出于安全考虑,对xhr请求进行限制
  • XHR请求:浏览器只会限制xhr(XmlHttpRequest)请求,只要不是xhr请求就不会有跨域问题
  • 跨域条件:域名、端口、协议任何一个不相同,浏览器会认为是跨域

备注:跨域并不是服务器不允许前端调用,可以从调试工具中发现xhr请求是有返回值的。

解决跨域问题

  • 修改浏览器配置:修改浏览器设置,放开跨域的限制
    • 例如:chrome –disable-web-security方式启动,chrome浏览器则不会限制跨域调用
  • 改用jsonp请求:浏览器只会对xhr请求进行限制,jsonp没有限制
    • jsonp:p表示pending补充,即jsonp是json的补充。
    • jsonp请求与json请求的区别:
      • jsonp请求类型为script,而json为xhr。
      • jsonp的content-type为application/javascript,而json为application/json
      • jsonp的请求url中会带callback参数,callback参数是jsonp的前后端的约定
    • jsonp前后端的修改:
      • 前端:需要将请求返回结果类型调整为jsonp,示例:$.ajax({dataType: “jsonp”})
      • 后端:需要将返回的json对象,调整为js代码(前端将返回结果当js代码执行)
  • 跨域条件:
    • 被调用方:服务方在请求结果的请求头,告诉请求方允许调用
    • 调用方:通过代理的方式,让浏览器觉得请求都是同一个服务端

jsonp协议

  • jsonp非官方协议,只是一个http请求约定
    • 请求方:发请求时需要传约定参数,默认为callback
    • 被请求方:服务方处理请求时,发现参数包含约定参数callback,则在请求返回时将响应结果修改为js函数内容,函数名为callback参数的值,函数的参数为要返回的json对象。
  • jsonp修改默认参数callback:
    • 前端:$.ajax({jsonp:”callback2”}); // 将默认的callback修改为callback2
    • 后端:在json切面类,super(“callback”)修改为super(“callback2”)
  • jsonp的原理:
    • jquery默认是页面加载时在head头部插入一个<script src="jsonp_url">这样的临时标签
    • 临时标签只有打断点能看到,页面加载完成时会执行json_url返回的内容,并删除script标签
    • 因此jsonp只支持get请求
  • jsonp的弊端
    • 需要服务端支持,如果服务端不支持,只是修改客户端的dataType是不行的。
    • 只支持get请求:有时候需要post请求,但jsonp是不支持的
    • 不是xhr请求:本质是script请求,所以无法支持xhr请求的特性,例如:异步调用、事件回调

跨域条件的解决思路

  • 请求的处理流程:client -> nginx | apache(HTTP服务器) -> tomcat | jboss(应用服务器)
    • client -> client-proxy nginx -> server proxy nginx -> tomcat
  • 被调用方解决思路:在response响应头中加入Access-Control-Allow-Origin、Access-Control-Allow-Methods字段
    • tomcat实现:
    • nginx配置:
    • apache配置:
  • 跨域请求的特点:
    • 请求端会在request header中加入Origin字段,值是当前前端页面的域名信息
    • 然后判断服务器的response header是否丰存在Access-Control-Allow的属性

简单请求和非简单请求

  • 简单请求:
    • 请求类型:GET、HEAD、POST
    • 请求Header满足以下两个条件
      • 无自定义头
      • Content-Type:text/plain、multipart/form-data、application/x-www-form-urlencoded
  • 非简单请求:(与简单请求相反)
    • 请求类型:PUT、DELETE
    • 带自定义请求头的ajax请求
    • 请求类型Content-Type:application/json,例如:发送json格式的ajax请求
  • 对于跨域请求:简单请求是先发请求后判断,但是对于非简单请求是先判断再发请求。
  • 请求类型为OPTION表示预检命令,对于复杂请求,浏览器会先发一次预检命令

被调用方Web Server设置方式

  • Access-Control-Allow-Origin = “*” 是无法满足cookie跨域的请求的
    • 支持cookie需要在Response响应头加入:Access-Control-Allow-Origin=”实际地址”,例如:”localhost:8081”
    • 支持cookie需要在Response响应头加入:Access-Control-Allow-Credentials=”true”
  • 通用的跨域调用的处理方式:注意cookie是被调用方的cookie
String origin = request.getHeader("Origin"); // 从请求头中获取客户端的Origin信息
if(StringUtils.isNotEmpty(origin)){
    response.setHeader("Access-Control-Allow-Origin", origin); // 判断是否是跨域请求
}
response.setHeader("Access-Control-Allow-Credentials", "true"); // 避免cookie请求报错
String headers = reuqest.getHeader("Access-Control-Allow-Headers");
if(StringUtils.isNotEmpty(headers)){
    response.setHeader("Access-Control-Allow-Headers", headers); // 通用的header
}
  • Spring框架对跨域的支持:直接在Controller类加上@CrossOrigin注解即可,此注解也可以加在方法上

被调用方Nginx及Apache设置方式

  • nginx添加如下配置,被调用方的Nginx服务器配置
server {
    listen 80;
    server_name xxx.com; // 服务端域名

    location /{ // url转发
        proxy_pass http://localhost:8080; // 将xxx.com转发至localhost:8080

        add_header Access-Control-Allow-Methods *;
        add_header Access-Control-Max-Age 3600; // option请求缓存时间
        add_header Access-Control-Allow-Credentials true;

        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Headers $http_access_control_allow_headers;

        if ($request_method = OPTIONS){
            return 200; // 预检命令直接返回200
        }
    }
}
  • Apache配置修改与nginx思路一样,具体设值查询下apache的配置说明。

调用方解决方案 - 隐藏跨域

  • 调用方的nginx配置如下:
server{
    listen 80;
    server_name ccc.com;

    location / { // 将调用方的ccc.com转发至localhost:8081,调用方需要通过ccc.com访问
        proxy_pass http://localhost:8081/; // 域名访问,通过客户端代理解决跨域问题
    }

    location /ajaxserver {
        proxy_pass http://localhost:8080/test; // 将ajax请求统一转发至服务端
    }
}
  • 注意:此时调用方(客户端)的ajax代码,需要将请求url设置为相对路径,示例:
var baseUrl = "/ajaxserver"; // 正常情况应该是将baseUrl设置为服务端的绝对url,示例:localhost:8080
// 设置为/表示此路径为相对地址,即localhost:8081,这样来看所有的请求就是都走的8081的/ajaxserver转发,就不满足跨域条件
$.getJSON(baseUrl + "/getData").then(function(){});

参考资料:http://www.cnblogs.com/oneword/archive/2012/12/03/2799443.html

猜你喜欢

转载自blog.csdn.net/bbirdsky/article/details/80561324