• 关于前后端分离跨域请求问题


    1.问题描述:

      在使用react开发时遇到了前后端分离post请求跨域的问题,导致请求无法正常完成。

    2.什么是跨域?

      当客户端向服务器发起一个网络请求,url会有包含三个主要信息:协议(protocol),域名(host),端口号(port)。当三部分都和服务器相同的情况下,属于同源。但是只要有一个不同,就属于构成了跨域调用。会受到同源策略的限制。

      同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用XMLHttpRequest请求)。

    3.几种跨域解决方式:

    3.1. jsonp请求:

      JSONP(json with padding 填充式json),利用了使用src引用静态资源时不受跨域限制的机制。主要在客户端搞一个回调做一些数据接收与操作的处理,并把这个回调函数名告知服务端,而服务端需要做的是按照javascript的语法把数据放到约定好的回调函数之中即可。jQuery很早之前就已经吧JSONP语法糖化了,使用起来会更加方便。

      jsonp的简单实现:

     1 // 回调函数
     2 function jsonpCallback(data) {
     3     console.log("jsonpCallback: " + data.name)
     4 }
     5 $("#submit").click(function() {
     6     var data = {
     7         name: $("#name").val(),
     8         id: $("#id").val()
     9     };
    10     $.ajax({
    11         url: 'http://localhost:3001/ajax/deal',
    12         data: data,
    13         dataType: 'jsonp',
    14         cache: false,
    15         timeout: 5000,
    16         // jsonp 字段含义为服务器通过什么字段获取回调函数的名称
    17         jsonp: 'callback',
    18         // 声明本地回调函数的名称,jquery 默认随机生成一个函数名称
    19         jsonpCallback: 'jsonpCallback',
    20         success: function(data) {
    21             console.log("ajax success callback: " + data.name)
    22         },
    23         error: function(jqXHR, textStatus, errorThrown) {
    24             console.log(textStatus + ' ' + errorThrown);
    25         }
    26     });
    27 });

    3.2 cors跨域资源共享

      CORS(Cross-origin resource sharing 跨域资源共享),依附于AJAX,通过添加HTTP Hearder部分字段请求与获取有权限访问的资源。CORS对开发者是透明的,因为浏览器会自动根据请求的情况(简单和复杂)做出不同的处理。CORS的关键是服务端的配置支持。由于CORS是W3C中一项较“新”的方案,以至于各大网页解析引擎还没有对其进行严格规格的实现,所以不同引擎下可能会有一些不一致。

      (1)Access-Control-Allow-Origin

    该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

      (2)Access-Control-Allow-Credentials

    该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

      (3)Access-Control-Expose-Headers

    该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

      java过滤器实现cors:

     1 public class CorsFilter implements Filter {
     2 
     3     public void init(FilterConfig filterConfig) throws ServletException {
     4     }
     5 
     6     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
     7         throws IOException, ServletException {
     8         HttpServletResponse httpResponse = (HttpServletResponse) response;
     9         httpResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");//设置允许跨域的域名,需要发送cookie信息,所以此处需要指定具体的域名,
    10         httpResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    11         httpResponse.setHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE, POST");//允许跨域的请求方式
    12         /**
    13          * ajax请求的时候如果带有xhrFields:{withCredentials:true},
    14          * 那么服务器后台在配置跨域的时候就必须要把Access-Control-Allow-Credentials这个请求头加上去
    15          */
    16         httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//允许发送Cookie信息
    17         httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // 支持HTTP 1.1.
    18         httpResponse.setHeader("Pragma", "no-cache"); // 支持HTTP 1.0. response.setHeader("Expires", "0");
    19         chain.doFilter(request, response);
    20     }
    21 
    22     public void destroy() {
    23         // TODO Auto-generated method stub
    24     }
    25 }

    jsonp和cors的不同:

      两者优点与缺点大致互补,放在一块介绍:

    1. JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器支持CORS,但IE10以下不支持CORS。
    2. JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被玩虐,但大部分情况下GET已经能满足需求了)
    3. JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。
    4. JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求
    5. 始终觉得安全性这个东西是相对的,没有绝对的安全,也做不到绝对的安全。毕竟JSONP并不是跨域规范,它存在很明显的安全问题:callback参数注入和资源访问授权设置。CORS好歹也算是个跨域规范,在资源访问授权方面进行了限制(Access-Control-Allow-Origin),而且标准浏览器都做了安全限制,比如拒绝手动设置origin字段,相对来说是安全了一点。
      但是回过头来看一下,就算是不安全的JSONP,我们依然可以在服务端端进行一些权限的限制,服务端和客户端也都依然可以做一些注入的安全处理,哪怕被攻克,它也只能读一些东西。就算是比较安全的CORS,同样可以在服务端设置出现漏洞或者不在浏览器的跨域限制环境下进行攻击,而且它不仅可以读,还可以写。

    3.3. react通过webpack配置代理实现跨域请求:

      代理的话其实也可以使用nginx反向代理,但是配置的时候发现请求过程总是处于(pending)等待状态,很久才能响应,不知是何原因引起,还未解决,如有大牛看到,肯请指教。

    nginx配置如下:

     1 location ^~/ {
     2              proxy_pass http://localhost:8080;
     3              proxy_redirect default ;
     4              proxy_set_header Host $host:80;
     5              proxy_set_header X-Real-IP $remote_addr;
     6              proxy_set_header REMOTE-HOST $remote_addr;
     7              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     8              
     9             
    10         }

      因为失败了,所以转而使用webpack代理配置:

      首先在webpack.config.js中进行配置:

    module.exports = {
      //。。。省略
    
    devServer: {
                port: '8081',
                host: '127.0.0.1',
                historyApiFallback: false,
                disableHostCheck: true,
                noInfo: false,
                stats: 'minimal',
                inline: true,
                //开启服务器的模块热替换(HMR)
                hot: true,
                // 和上文 output 的“publicPath”值保持一致
                publicPath: context,
                proxy: {
                    '/sharemeeting/*': {
                        target: "http://localhost:8080",
                        changeOrigin: true,
                        secure: false
                    }
                }
            }
        //。。。省略
    };

       proxy中,'/sharemeeting/*'是过滤要代理的请求的路径,tartget为代理后的目标服务器地址,包括协议、域名、端口号信息。如果发起的请求为http://localhost:8081/sharemeeting/getTest.do。那么将会被代理为http://localhost:8080/sharemeeting/getTest.do

      fetch请求

     1       let url = "/sharemeeting/login/getTest.do";
     2         fetch(url,{
     3             method: 'POST',
     4             headers: {
     5                 'Accept': 'application/json',
     6                 'Content-Type': 'application/json'
     7             },
     8             credentials: 'include',
     9             body: JSON.stringify({})
    10         }).then((response) => response.json()) //把response转为json
    11           .then((responseData) => { // 上面的转好的json
    12                 if(responseData.status === false){
    13                     window.location.href="#/";
    14                 }else{
    15                     this.reset(responseData.data);
    16                 }
    17             }).catch((error)=> {
    18             window.location.href="#/";
    19         })

      请求的url要像上面这样定义,如果这样定义,这样请求的时候会自己添加上协议和请求的域名端口号,

    ======================================参考=================================

    1.https://www.zhihu.com/question/41992168 JS跨域方案JSONP与CORS的各自优缺点以及应用场景?

    2.http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html 浏览器同源政策及其规避方法

    3.http://www.ruanyifeng.com/blog/2016/04/cors.html 跨域资源共享 CORS 详解

    4.https://www.jianshu.com/p/3bdff821f859 Webpack dev server使用http-proxy解决跨域问题

  • 相关阅读:
    cookies和re
    2018福大软工实践-第一次作业之自我介绍
    2018福大软工实践-第一次作业
    Linux shell脚本编程
    课程第八次作业
    课程第七次作业
    课程第六次作业
    课程第五次作业
    四则运算课程第四次作业
    第三次课程作业
  • 原文地址:https://www.cnblogs.com/caijh/p/8358416.html
Copyright © 2020-2023  润新知