转:https://blog.csdn.net/csdn_ds/article/category/6937392/3

在工作中,大家应该都遇到过ajax跨域问题,浏览器的错误如下:

XMLHttpRequest cannot load http://目标地址No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://当前页面地址'  is therefore not allowed access.

为什么会出现跨域问题

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。

在此说明一下,所谓的同源,指的是域名、协议、端口均相等。举例如下:

http://www.abc.com/a/b 调用 http://www.abc.com/d/c(非跨域)

http://www.abc.com/a/b 调用 http://www.def.com/d/c (跨域:域名不一致)

http://www.abc.com:81/a/b 调用 http://www.abc.com:82/d/c (跨域:端口不一致)

http://www.abc.com/a/b 调用 https://www.abc.com/d/c (跨域:协议不同)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

在一个http请求中,http头部Referer或Origin字段标识了当前域名,Host字段标识了此时请求的域名。

故,如果我们在当前的js页面,通过ajax请求第三方的数据,就会出现浏览器的跨域问题。

解决跨域问题

解决跨域问题,有如下三种方式:

1、使用jsonp

2、服务器代理

3、在服务端设置response header中Access-Control-Allow-Origin字段。

使用jsonp

jsonp解决跨域问题的原理是,浏览器的script标签是不受同源策略限制的,我们可以在script标签中访问任何域名下的资源文件。利用这一特性,用script标签从服务器中请求数据,同时服务器返回一个带有方法和数据的js代码,请求完成,调用本地的js方法,来完成数据的处理。

前端实现,以Jquery的ajax方法为例:

  1.  
    $.ajax({
  2.  
    url:"",
  3.  
    dataType:'jsonp',
  4.  
    data:'',
  5.  
    jsonp:'callback', //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
  6.  
     
  7.  
    success:function(result) {
  8.  
    //成功的处理
  9.  
    },
  10.  
    error:function(){
  11.  
    //错误处理
  12.  
    }
  13.  
    });

服务端此时返回的不能是普通的json字符串,而是一段可以被前端js执行的一段js代码。

比较一下json与jsonp格式的区别:

json格式:

  1.  
    {
  2.  
    "message":"获取成功",
  3.  
    "state":"1",
  4.  
    "result":{"name":"工作组1","id":1,"description":"11"}
  5.  
    }

jsonp格式:

  1.  
    callback({
  2.  
    "message":"获取成功",
  3.  
    "state":"1",
  4.  
    "result":{"name":"工作组1","id":1,"description":"11"}
  5.  
    })

从格式来看,jsonp是在json的基础上包装了一个方法名,此方法名是前端请求传过来的,如请求地址为:http://localhost:9999/tookApp/tbk/getItem?callback=JSONP_CALLBACK,那么方法名就是JSONP_CALLBACK。

下面提供一段java代码,对象转jsonp的工具类:

  1.  
    package com.tooklili.app.web.util;
  2.  
     
  3.  
    import javax.servlet.http.HttpServletRequest;
  4.  
     
  5.  
    import org.apache.commons.lang.StringUtils;
  6.  
    import org.springframework.web.context.request.RequestContextHolder;
  7.  
    import org.springframework.web.context.request.ServletRequestAttributes;
  8.  
     
  9.  
    import com.fasterxml.jackson.databind.util.JSONPObject;
  10.  
     
  11.  
    /**
  12.  
    *
  13.  
    * @author ding.shuai
  14.  
    * @date 2016年8月15日上午9:47:02
  15.  
    */
  16.  
    public class AppUtil {
  17.  
     
  18.  
    /**
  19.  
    * 判断json字符串是否需要转化成jsonp格式
  20.  
    * @param request
  21.  
    * @param result
  22.  
    * @return
  23.  
    */
  24.  
    public static Object conversionJsonp(Object result){
  25.  
    HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
  26.  
    return conversionJsonp(request, result);
  27.  
    }
  28.  
     
  29.  
     
  30.  
    public static Object conversionJsonp(HttpServletRequest request,Object result){
  31.  
    String callback = request.getParameter("callback");
  32.  
    if(StringUtils.isNotEmpty(callback)){
  33.  
    return new JSONPObject(callback, result);
  34.  
    }
  35.  
    return result;
  36.  
    }
  37.  
    }

jsonp的缺点:

1、JSONP是一种非官方的方法,而且这种方法只支持GET方法,不如POST方法安全。(从实现机制就可明白)。

2、JSONP的实现需要服务器配合,如果是访问的是第三方的服务器,我们没有修改服务器的权限,那么这种方式是不可行的。

服务器代理

这种方式运用的就是服务器的反向代理技术,控制客户端和服务器的访问都从代理服务器经过,比如用nginx作为服务器代理,在nginx上配置客户端和第三方服务的反向代理,这样就可保证客户端、第三方是同源的了,同一个源,都来自代理服务器。

关于nginx的反向代理配置,可访问我的这篇博客:http://blog.csdn.net/csdn_ds/article/details/58605591

服务器代理的缺点:

开发比较麻烦,对开发环境比较严格,需要在本机上配置代理服务器。

优点:

完美解决使用jsonp,第三方服务没有修改权限的问题。程序的代码侵入性小,代码级别不需要考虑跨域问题。

在服务端设置response headerAccess-Control-Allow-Origin字段

在被请求的Response Header中加入如下代码:

  1.  
    // 指定允许其他域名访问
  2.  
    response.setHeader("Access-Control-Allow-Origin", "*");
  3.  
    // 响应类型
  4.  
    response.setHeader("Access-Control-Allow-Methods", "POST");
  5.  
    // 响应头设置
  6.  
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");

如果所有请求都想让其他域名的服务通过浏览器ajax请求到,可以通过Filter统一设置response header。

  1.  
    package com.tooklili.app.web.filter;
  2.  
     
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    import javax.servlet.Filter;
  6.  
    import javax.servlet.FilterChain;
  7.  
    import javax.servlet.FilterConfig;
  8.  
    import javax.servlet.ServletException;
  9.  
    import javax.servlet.ServletRequest;
  10.  
    import javax.servlet.ServletResponse;
  11.  
    import javax.servlet.http.HttpServletResponse;
  12.  
     
  13.  
    /**
  14.  
    * 设置公共属性的过滤器
  15.  
    * @author shuai.ding
  16.  
    *
  17.  
    * @date 2017年6月21日上午11:02:27
  18.  
    */
  19.  
    public class CommonSetFilter implements Filter{
  20.  
     
  21.  
    @Override
  22.  
    public void init(FilterConfig filterConfig) throws ServletException {
  23.  
    }
  24.  
     
  25.  
    @Override
  26.  
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  27.  
    throws IOException, ServletException {
  28.  
    //解决跨域问题
  29.  
    HttpServletResponse httpServletResponse =(HttpServletResponse)response;
  30.  
    // 指定允许其他域名访问
  31.  
    httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
  32.  
    // 响应类型
  33.  
    httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST");
  34.  
    // 响应头设置
  35.  
    httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
  36.  
     
  37.  
    chain.doFilter(request, response);
  38.  
    }
  39.  
     
  40.  
    @Override
  41.  
    public void destroy() {
  42.  
    }
  43.  
     
  44.  
    }

此处说明一下,笔者亲测:只设置Access-Control-Allow-Origin属性也是可以的。

Access-Control-Allow-Origin:* 表示允许任何域名跨域访问

如果需要指定某域名才允许跨域访问,只需把Access-Control-Allow-Origin:*改为Access-Control-Allow-Origin:允许的域名

例如:response.setHeader(“Access-Control-Allow-Origin”,”http://www.client.com”);

缺点:

1、此种解决跨域方案,需要浏览器支持H5,因为这是HTML5解决跨域的方式,如果产品面向的是PC端,这种方式可能就不是一个好的解决方案,如果面向的是手机端,此方法不为一个简单、粗暴的好方式。

2、设置*,存在安全隐患。

总结

综上三种解决跨域的方案,个人感觉使用服务代理最好,没有破坏浏览器的安全策略,但这个对开发环境要高一点。设置response header的方式,根据具体情况分析,要考虑清楚产品面向的用户。对于jsonp这种方式,虽然没有破坏浏览器的安全策略,但只支持get方式的请求,有点不能接受,因为get传输有参数长度的限制,同时又要考虑传输中文的乱码问题,但如果项目中只是简单的查询、展示,这种方式还是可以考虑的。

其他好文推荐:

Access-Control-Allow-Origin 跨域设置多域名:http://www.jianshu.com/p/b587dd1b7086

ajax跨域问题以及解决方案的更多相关文章

  1. ajax 跨域访问的解决方案

    ajax 跨域访问的解决方案 一.什么是跨域: 1.什么样的请求属于跨域: 域名,端口有任何一个不相同都属于跨域: 二.跨域的常用几种解决方案: 1.jsonp: 2.iframe: 3.webcon ...

  2. Ajax跨域问题及解决方案 asp.net core 系列之允许跨越访问(Enable Cross-Origin Requests:CORS) c#中的Cache缓存技术 C#中的Cookie C#串口扫描枪的简单实现 c#Socket服务器与客户端的开发(2)

    Ajax跨域问题及解决方案   目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP ...

  3. PHP Ajax 跨域问题最佳解决方案 【摘自菜鸟教程】

    PHP Ajax 跨域问题最佳解决方案 分类 编程技术 http://www.runoob.com/w3cnote/php-ajax-cross-border.html 本文通过设置Access-Co ...

  4. ajax跨域原理以及解决方案

    说明 跨域主要是由于浏览器的“同源策略”引起,分为多种类型,本文主要探讨Ajax请求跨域问题 前言 强烈推荐阅读参考来源中的文章,能够快速帮助了解跨域的原理 参考来源 本文参考了以下来源 浏览器同源政 ...

  5. [转载]JQuery的Ajax跨域请求的解决方案

    今天在项目中需要做远程数据加载并渲染页面,直到开发阶段才意识到ajax跨域请求的问题,隐约记得Jquery有提过一个ajax跨域请求的解决方式,于是即刻翻出Jquery的API出来研究,发现JQuer ...

  6. ajax跨域访问的解决方案

    今天的工作中要访问摄像机内部的一个web站点,这就涉及到jquery的ajax跨域访问的问题.我使用的是jquery1.7的版本,下面总结如下: 问题一:一开始用IE调试,总是返回No Transpo ...

  7. 有关Ajax跨域请求的解决方案

    前言 最近博主在赶项目进度.所以微信二次开发那边的博文一直没有更新.后续时间会慢慢记录这个学习历程的.来年公司要开发微信小程序.到时也会记录一下历程. 闲话少说,今天在工作中遇到了SpringMVC接 ...

  8. Ajax跨域问题及解决方案

    目录 复现Ajax跨域问题 Ajax跨域介绍 Ajax跨域解决方案 一. 在服务端添加响应头Access-Control-Allow-Origin 二. 使用JSONP解决 小结 复现Ajax跨域问题 ...

  9. ajax跨域请求的解决方案

    一直打算改造一下自己传统做网站的形式. 我是.Net程序员,含辛茹苦数年也没混出个什么名堂. 最近微信比较火, 由于现在大环境的影响和以前工作的总结和经验,我打算自己写一个数据,UI松耦合的比较新潮的 ...

随机推荐

  1. 业务代码的救星——Java 对象转换框架 MapStruct 妙用

    简介 在业务项目的开发中,我们经常需要将 Java 对象进行转换,比如从将外部微服务得到的对象转换为本域的业务对象 domain object,将 domain object 转为数据持久层的 dat ...

  2. java基础-多线程一

    什么是线程 说到线程就不得不说下进程了, 大家都知道,许许多多的线程组合在一起就成了一个进程,进程是由操作系统进行资源操作的一个最小的单位,线程则是比进程更小的实际执行操作的单位:每个线程都有自己的堆 ...

  3. addTarget原理

    addTarget原理: 当一个控件addTarget时,先到runLoop注册,然后runLoop才会监听该事件,事件处理按照响应者链条   以下以button为例图解:

  4. python中,一个函数想使用另一个函数中的变量

    问题: 第一个函数中用到了变量a:第二个函数也想使用变量a. 解决方法: 在第一个函数中将变量a定义为全局变量,然后在第二个函数中,也写上global a即可. 示例: def func1(): gl ...

  5. HDU - 2824 The Euler function 欧拉函数筛 模板

    HDU - 2824 题意: 求[a,b]间的欧拉函数和.这道题卡内存,只能开一个数组. 思路: ϕ(n) = n * (p-1)/p * ... 可利用线性筛法求出所有ϕ(n) . #include ...

  6. 玲珑杯 1137 - Sin your life(数学)

    题目链接:http://www.ifrog.cc/acm/problem/1137 题解:设m=n-z sin(x)+sin(y)=sin(m-y)+sin(y)利用公式得最大值为sqrt(sin(m ...

  7. LeetCode380 常数时间插入、删除和获取随机元素

    LeetCode380 常数时间插入.删除和获取随机元素 题目要求 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插 ...

  8. briup_jdbc自建工具类终极版

    总结:此次构建工具类,难点在于查询,所需要的功能是 不管是 oracle还是mysql 都可以连接,并且 提供所需要的实体类,都可以将查询内容封装到实体类中去 遇到的难点  连接时,是从prppert ...

  9. 二进制协议gob及msgpack介绍

    本文主要介绍二进制协议gob及msgpack的基本使用. 最近在写一个gin框架的session服务时遇到了一个问题,Go语言中的json包在序列化空接口存放的数字类型(整型.浮点型等)都序列化成fl ...

  10. Golang 数组 切片 字典 基本知识点

    数组 数组的声明 var arrayName [arraySize]dataType eg: var array1 [5]int 在声明数组时,必须指定数组名,数组长度和数组元素的类型. 数组的初始化 ...