zoukankan      html  css  js  c++  java
  • ajax跨域问题

    解决ajax跨域问题【5种解决方案】

     

    什么是跨域问题?
    跨域问题来源于JavaScript的"同源策略",即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题。

    查看浏览器开发者工具Console报错:

    Failed to load http://a.a.com:8080/A/FromServlet?userName=123: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://b.b.com:8080' is therefore not allowed access.

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

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

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

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

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

    跨域问题怎么解决?
    1、响应头添加Header允许访问

    2、jsonp 只支持get请求不支持post请求

    3、httpClient内部转发

    4、使用接口网关——nginx、springcloud zuul   (互联网公司常规解决方案)

    解决方式1:响应头添加Header允许访问
    跨域资源共享(CORS)Cross-Origin Resource Sharing

    response.addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
    response.addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式
     

    解决方式2:jsonp 只支持get请求不支持post请求
    用法:①dataType改为jsonp     ②jsonp : "jsonpCallback"————发送到后端实际为http://a.a.com/a/FromServlet?userName=644064&jsonpCallback=jQueryxxx     ③后端获取get请求中的jsonpCallback    ④构造回调结构

    $.ajax({
    type : "GET",
    async : false,
    url : "http://a.a.com/a/FromServlet?userName=644064",
    dataType : "jsonp",//数据类型为jsonp
    jsonp : "jsonpCallback",//服务端用于接收callback调用的function名的参数
    success : function(data) {
    alert(data["userName"]);
    },
    error : function() {
    alert('fail');
    }
    });
     

    //后端
    String jsonpCallback = request.getParameter("jsonpCallback");
    //构造回调函数格式jsonpCallback(数据)
    resp.getWriter().println(jsonpCallback+"("+jsonObject.toJSONString()+")");
    JSONP实现原理
    在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,即一般的ajax是不能进行跨域请求的。但 img、iframe 、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。利用<script>标签的开放策略,我们可以实现跨域请求数据,当然这需要服务器端的配合。 Jquery中ajax的核心是通过 XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的 js脚本。

      当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用 JSONP模式来请求数据的时候服务端返回的是一段可执行的JavaScript代码。因为jsonp 跨域的原理就是用的动态加载<script>的src ,所以我们只能把参数通过url的方式传递,所以jsonp的 type类型只能是get !

    示例:

    $.ajax({

        url: 'http://192.168.10.46/demo/test.jsp',        //不同的域

        type: 'GET',                                                        // jsonp模式只有GET 是合法的

        data: {

            'action': 'aaron'

        },

        dataType: 'jsonp',                                              // 数据类型

        jsonp: 'jsonpCallback',                                     // 指定回调函数名,与服务器端接收的一致,并回传回来

    })

    其实jquery 内部会转化成

    http://192.168.10.46/demo/test.jsp?jsonpCallback=jQuery202003573935762227615_1402643146875&action=aaron

    然后动态加载

    <script type="text/javascript"src="http://192.168.10.46/demo/test.jsp?jsonpCallback= jQuery202003573935762227615_1402643146875&action=aaron"></script>

    然后后端就会执行jsonpCallback(传递参数 ),把数据通过实参的形式发送出去。

      使用JSONP 模式来请求数据的整个流程:客户端发送一个请求,规定一个可执行的函数名(这里就是 jQuery做了封装的处理,自动帮你生成回调函数并把数据取出来供success属性方法来调用,而不是传递的一个回调句柄),服务器端接受了这个 jsonpCallback函数名,然后把数据通过实参的形式发送出去

    (在jquery 源码中, jsonp的实现方式是动态添加<script>标签来调用服务器提供的 js脚本。jquery 会在window对象中加载一个全局的函数,当 <script>代码插入时函数执行,执行完毕后就 <script>会被移除。同时jquery还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的 Ajax请求一样工作。)

    解决方式3:httpClient内部转发
    实现原理很简单,若想在B站点中通过Ajax访问A站点获取结果,固然有ajax跨域问题,但在B站点中访问B站点获取结果,不存在跨域问题,这种方式实际上是在B站点中ajax请求访问B站点的HttpClient,再通过HttpClient转发请求获取A站点的数据结果。但这种方式产生了两次请求,效率低,但内部请求,抓包工具无法分析,安全。

    $.ajax({
    type : "GET",
    async : false,
    url : "http://b.b.com:8080/B/FromAjaxservlet?userName=644064",
    dataType : "json",
    success : function(data) {
    alert(data["userName"]);
    },
    error : function() {
    alert('fail');
    }
    });
     

    @WebServlet("/FromAjaxservlet")
    public class FromAjaxservlet extends HttpServlet{


    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    try {
    //创建默认连接
    CloseableHttpClient httpClient = HttpClients.createDefault();
    //创建HttpGet对象,处理get请求,转发到A站点
    HttpGet httpGet = new HttpGet("http://a.a.com:8080/A/FromServlet?userName="+req.getParameter("userName"));
    //执行
    CloseableHttpResponse response = httpClient.execute(httpGet);
    int code = response.getStatusLine().getStatusCode();
    //获取状态
    System.out.println("http请求结果为:"+code);
    if(code == 200){
    //获取A站点返回的结果
    String result = EntityUtils.toString(response.getEntity());
    System.out.println(result);
    //把结果返回给B站点
    resp.getWriter().print(result);
    }
    response.close();
    httpClient.close();
    } catch (Exception e) {
    }
    }
    }
     

    解决方式4:使用nginx搭建企业级接口网关方式
    www.a.a.com不能直接请求www.b.b.com的内容,可以通过nginx,根据同域名,但项目名不同进行区分。什么意思呢?这么说可能有点抽象。假设我们公司域名叫www.nginxtest.com

    当我们需要访问www.a.a.com通过www.nginxtest.com/A访问,并通过nginx转发到www.a.a.com

    当我们需要访问www.b.b.com通过www.nginxtest.com/B访问,并通过nginx转发到www.a.a.com

    我们访问公司的域名时,是"同源"的,只是项目名不同,此时项目名的作用只是为了区分,方便转发。如果你还不理解的话,先看看我是怎么进行配置的:

    server {
    listen 80;
    server_name www.nginxtest.com;
    location /A {
    proxy_pass http://a.a.com:81;
    index index.html index.htm;
    }
    location /B {
    proxy_pass http://b.b.com:81;
    index index.html index.htm;
    }
    }
    我们访问以www.nginxtest.com开头且端口为80的网址,nginx将会进行拦截匹配,若项目名为A,则分发到a.a.com:81。实际上就是通过"同源"的域名,不同的项目名进行区分,通过nginx拦截匹配,转发到对应的网址。整个过程,两次请求,第一次请求nginx服务器,第二次nginx服务器通过拦截匹配分发到对应的网址。

    解决方式5:使用Spring Cloud zuul接口网关

    我话讲完!谁赞成?谁反对?
  • 相关阅读:
    将PHP文件生成静态文件源码
    Entity Framework Code First 学习日记(6)一对多关系
    Entity Framework Code First 学习日记(5)
    Entity Framework Code First 学习日记(3)
    Entity Framework Code First 学习日记(7)多对多关系
    Entity Framework Code First学习日记(2)
    Entity Framework Code First 学习日记(8)一对一关系
    Entity Framework Code First 学习日记(9)映射继承关系
    Entity Framework Code First 学习日记(10)兼容遗留数据库
    Entity Framework Code First 学习日记(4)
  • 原文地址:https://www.cnblogs.com/wffzk/p/14872321.html
Copyright © 2011-2022 走看看