跨域问题以及解决方案

版权声明:本文为博主原创,转载加个原文章链接。 https://blog.csdn.net/xiaolongbaobushibao/article/details/82352799

跨域问题以及解决方案

一、什么是跨域

因为安全问题,非同源的网站发送请求数据,会被浏览器拒绝。

二、跨域的原因

就是跨域名,跨端口,跨协议。举个例子:
http://www.baidu.com/hello/world.html

请求地址 形式 是否跨域
http://www.baidu.com/test/a.html 同一域名,不同文件夹 非跨域
http://www.baidu.com/hello/kitty.html 同一域名,同个文件夹 非跨域
http://test.baidu.com/hello/world.html 非同个域名,文件夹一样也没用 跨域
http://www.baidu.com:8081/hello/world.html 同一域名,端口不一样 跨域
https://www.baidu.com/hello/world.html 不同的协议、同个域名,文件夹一样 跨域

域名要一致,端口要一致,协议(http、https要一致)才不是跨域。

三、如何解决ajax跨域

3.1、 jsonp

古老又神奇的解决方案,实际不推荐。如果要用,可以使用类似JQuery对jsonp封装好的类库来处理。

$.ajax({
    url: "http://localhost:8080/student",
    type: "GET",
    dataType: "jsonp", //指定服务器返回的数据类型
    success: function (data) {
       console.log(JSON.stringify(data));//获取得到的数据
    }
});

这时候你会在network看到https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?callback=jQuery30003244305640945704_1531990977401&tel=15510588888&_=1531990977402;
内部操作应该是生成一个全局的方法:jQuery30003244305640945704_1531990977401成功的时候回调这个方法,也就是

jQuery30003244305640945704_1531990977401(data){
    console.log(JSON.stringify(data));//获取得到的数据
}

当请求访问成功的时候会调用这个方法。之前用过一次,被动使用。主要是因为对接的技术方只提供这个方式,很显然他们说什么都对…

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>

</head>
<body>
   <h1>测试jsonp</h1>
    <ul>
         <li class="num">手机号码: <span></span></li>            
         <li class="province">归属省份: <span></span></li>
         <li class="operators">运 营 商: <span></span></li>

         <li class="num2">手机号码: <span></span></li>            
         <li class="province2">归属省份: <span></span></li>
         <li class="operators2">运 营 商: <span></span></li>
    </ul>

    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<script>
   function testjsonpcallback(data){
     var province = data.province,
       operators = data.catName,
       num = data.telString;

      $('.num span').html(num);
      $('.province span').html(province);
      $('.operators span').html(operators);

   }
$(function(){
    $.ajax({
        url: "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm",
        type: "GET",
        data:{tel:"15510588888"},
        dataType: "jsonp", //指定服务器返回的数据类型
        success: function (data) {
            console.log(JSON.stringify(data));//获取得到的数据
              var province = data.province,
              operators = data.catName,
              num = data.telString;

              $('.num2 span').html(num);
              $('.province2 span').html(province);
              $('.operators2 span').html(operators);
        }
    });
})
</script>
 <script src ="http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15510588888&callback=testjsonpcallback" type="text/javascript"></script>
</body>
</html>
  • 优点
    • 兼容性好,古董浏览器也能支持
    • 后端不用特殊支持
  • 缺点
    • 只支持GET
    • 只支持HTTP
    • 返回没有状态码

3.2、后端配置允许跨域

  • 后端过滤器

后端去掉所有的跨域过滤。生产环境请禁用跨域!


public void doFilter(ServletRequest req, ServletResponse res,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) res;
        res.setHeader("Access-Control-Allow-Origin", originHeader);
        res.setContentType("application/json;charset=UTF-8");
        res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");//表明服务器支持的所有头信息字段
        res.setHeader("Access-Control-Allow-Credentials", "true"); //如果要把Cookie发到服务器,需要指定Access-Control-Allow-Credentials字段为true;
        res.setHeader("XDomainRequestAllowed","1");
        chain.doFilter(req, res);
        return;
    }
  • 参数说明

    • "Access-Control-Allow-Origin"表明它允许”http://demo.com“发起跨域请求,* 为所有

    • "Access-Control-Max-Age"表明在3628800秒内,不需要再发送预检验请求,可以缓存该结果

    • "Access-Control-Allow-Methods"表明它允许GET、PUT、DELETE的外域请求

    • "Access-Control-Allow-Headers"表明它允许跨域请求包含content-type头

  • 前端使用

    • ajax

      $.ajax({
          url: config.url,
          xhrFields: { //请求支持跨域
              withCredentials: true
          },
          type: "GET",
          data:{tel:"15510588888"},
          success: function (rs) {},
          error: function (xhr, textStatus, errorThrown) {},
          complete: function (data) {}
      })
    • fetch

      fetch(url,{
          method: 'POST',
          mode: 'cors',//跨域模式
          credentials: 'include',//带上cookie
          headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
          },
          body: JSON.stringify(postData)
      })

3.3、nginx反向代理(推荐)

反向代理是不用改任何项目代码的,生产环境用的比较多。测试环境也可以使用webpack(node)的代理,此处仅介绍nginx反向代理。
- 反向代理
- 图
image

  • 发什么什么事?
    • 客户端处于域www.baidu.com 发起www.baidu.com/api请求
    • nginx 监听到了这个请求,中间代理给了www.google.com/api
    • www.google.com/api返回数据给nginx
    • nginx 返回数据给客户端;
  • 换成我们常用的开发环境(用nginx做容器):
    • 客户端处于域127.0.0.1:3000 发起127.0.0.1:3000/api请求
    • nginx 监听到了这个请求,中间代理给了127.0.0.1:8080/api
    • 127.0.0.1:8080/api返回数据给nginx
    • nginx 返回数据给客户端;
  • 用node 或者 tomcat等容器
    • 用nginx监听4000端口:127.0.0.1:4000,所有静态资源代理到127.0.0.1:3000,所有动态资源(接口什么的)代理到127.0.0.1:8080
    • 客户端处于域127.0.0.1:3000 发起127.0.0.1:4000/api请求
    • nginx 监听到了这个请求,中间代理给了127.0.0.1:8080/api
    • 127.0.0.1:8080/api返回数据给nginx
    • nginx 返回数据给客户端;
  • 配置
    server {
        listen       4000;
        #日志开启,方便调试Nginx配置
        error_log  logs/xxx.log notice;
        rewrite_log on;

        #正则匹配:~开头,原则是按配置顺序进行匹配。
        #先匹配静态资源,剩余的发送给动态资源

        # 所有请求 /api/结尾的都转发给后端服务
        location ~ /api/{

            set $context $1;

            proxy_pass http://192.168.1.171:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;          
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            client_max_body_size 100m;
            proxy_cookie_path ~.* /$context/;# cookie也有路径,路径不一样请求带的cookie可能丢失
        }

        # 拦截所有静态资源的请求到指定目录
        location ~ ^/(.+\.(?:html|css|jpeg|jpg|png|gif|js|eot|otf|ttf|woff|svg))$ {
            alias   usr/front/dist/$1;
            index  index.html index.htm;
        }
    }

3.4 修改document.domain

前提条件:客户端与服务端必须在一个一级域名、协议、端口都要一致,否则无法修改document.domain进行跨域。只能跨二级域名访问。

PS,一级域名:baidu.com、二级域名:tieba.baidu.com

console.log(document.domain);//"blog.csdn.net"
//修改域
document.domain = "csdn.net";//document.domain : "csdn.net"

//修改域
document.domain = "baidu.com";
//Uncaught DOMException: Failed to set the 'domain' property on 'Document': 
//'baidu.com' is not a suffix of 'csdn.net'.

修改domain只能做到近亲之间的跨域,跨度太大就不允许了。

酒要一口一口地喝,路要一步一步走,步子迈得太大,会扯着蛋–《让子弹飞》

猜你喜欢

转载自blog.csdn.net/xiaolongbaobushibao/article/details/82352799