• Ajax——AJAX跨域问题


    1. 跨域问题描述

    1.1 跨域的含义?

    • 跨域是指从一个域名的网页去请求另一个域名的资源。比如从百度(https://baidu.com)页面去请求京东(https://www.jd.com)的资源。
    • 在这里插入图片描述

    1.2 哪些访问存在跨域问题

    • 通过超链接或者form表单提交或者window.location.href的方式进行跨域是不存在问题的。但在一个域名的网页中的一段js代码发送ajax请求去访问另一个域名中的资源,由于同源策略的存在导致无法跨域访问,那么ajax就存在这种跨域问题。

    2. 同源问题

    • 同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,同源就是协议、域名和端口都相同。

    2.1 同源策略作用

    • 如果你刚刚在网银输入账号密码,查看了自己还有账户余额,紧接着访问一些不规矩的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。所以,从安全的角度来讲,同源策略是有利于保护网站信息的。

    2.2 同源判断

    • 区分同源和不同源的三要素

      • 协议
      • 域名
      • 端口号
    • 协议一致,域名一致,端口号一致,三个要素都一致,才是同源,其它一律都是不同源

    URL1URL2是否同源描述
    http://localhost:8080/a/index.htmlhttp://localhost:8080/a/first同源协议 域名 端口一致
    http://localhost:8080/a/index.htmlhttp://localhost:8080/b/first同源协议 域名 端口一致
    http://www.myweb.com:8080/a.jshttps://www.myweb.com:8080/b.js不同源协议不同
    http://www.myweb.com:8080/a.jshttp://www.myweb.com:8081/b.js不同源端口不同
    http://www.myweb.com/a.jshttp://www.myweb2.com/b.js不同源域名不同
    http://www.myweb.com/a.jshttp://crm.myweb.com/b.js不同源子域名不同

    3. 解决跨域问题

    说明:

    • 在模拟两个服务器时,一共创建了两个模块a和b,并且配置了两个Tomcat,一个运行模块a(主要是模拟前端代码请求),一个运行模块b(主要是模拟后端服务器做出的响应)

    3.1 方案一:设置响应头

    • 核心原理:跨域访问的资源允许你跨域访问。
    • 实现代码:
      //设置响应头,允许Ajax跨域请求
        response.setHeader("Access-Control-Allow-Origin","http://localhost:8080");
    
    • 1
    • 2

    模拟实现核心代码:

    // 使用ES6新特性:箭头函数
    window.onload = () => {
        document.getElementById("btn").onclick = () => {
            let xmlHttpRequest = new XMLHttpRequest();
            // 2. 注册回调函数
            xmlHttpRequest.onreadystatechange = () => {
                if (xmlHttpRequest.readyState == 4) {
                    if (xmlHttpRequest.status >= 200 && xmlHttpRequest.status < 300) {
                        document.getElementById("mydiv").innerHTML = xmlHttpRequest.responseText
                    }
                }
            }
            // 3. 开启通道
            xmlHttpRequest.open("GET", "http://localhost:8081/b/hello", true)
            // 4. 发送请求
            xmlHttpRequest.send()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    同源

    • 响应给前端数据
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            //设置响应头,允许Ajax跨域请求
            response.setHeader("Access-Control-Allow-Origin","http://localhost:8080");
    
    //        response.getWriter().print("hello,ajax");
            response.getWriter().print("{\"username\":\"zhangsan\"}");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.2 方案二:使用jsonp

    jsonp简介:

    • jsonp:json with padding(带填充的json)
    • jsonp不是一个真正的ajax请求。只不过可以完成ajax的局部刷新效果。可以说jsonp是一种类ajax请求的机制。
    • jsonp不是ajax请求,但是可以完成局部刷新的效果,并且可以解决跨域问题。
    • 注意:jsonp解决跨域的时候,只支持GET请求。不支持post请求。

    代码实现:

    • 发送请求
    <script type="text/javascript">
        function sayHello(data){
            alert("hello"+data.name)
        }
    </script>
    
    <!--使用jsonp实现跨域请求:src:向 b web应用的jsonp程序发送请求-->
    <script type="text/javascript" src="http://localhost:8081/b/jsonp1?fun=sayHello">
    
        /*从bWEB服务器会返回一个js代码*/
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 响应数据
    String fun = request.getParameter("fun");
    
    response.getWriter().print(fun+"({\"name\":\"json\"})");
    
    • 1
    • 2
    • 3

    3.3 方案三:jQuery封装的jsonp

    使用jQuery封装的jsonp,在发送请求时,URL会被默认添加上callback=jQuery36003529724253140707_1660986216632&_=1660986216637,【创建一个callback函数】这就导致在后端响应数据时,需要把json格式的数据响应给callback函数,之后函数会再去调用ajax请求中的success属性。

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>jQuery的jsonp封装解决Ajax的跨域问题title>
    head>
    <body>
    <script type="text/javascript" src="/a/js/jquery-3.6.0.min.js">script>
    
    <script type="text/javascript">
    
        $(function () {
            $("#btn").click(function () {
                //默认发送地址
                //http://localhost:8081/b/jsonp3?callback=jQuery36003529724253140707_1660986216632&_=1660986216637
                //jQuery会默认生成一个callback函数,然后执行success中的内容
                $.ajax({
                    type: "GET",//jsonp仅仅支持get
                    url: "http://localhost:8081/b/jsonp3",
                    dataType: "jsonp",//指定数据类型是jsonp形式
                    //另外自己也可以指定函数名和函数,不适用默认的callback
                    /*jsonp: "fun",
                    jsonpCallback:"sayHello",*/
                    success: function (data) {
                        $("#mydiv").html("欢迎,"+data.username)
                    }
                })
            })
        })
    script>
    
    <button id="btn">jQuery库封装的jsonpbutton>
    <div id="mydiv">div>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 后端响应
    @WebServlet("/jsonp3")
    public class JSONP3Servlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String callback = request.getParameter("callback");
    
            response.getWriter().print(callback+"({\"username\":\"zhangsan\"})");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.4 方案四:使用代理机制

    理解代理机制:
    在这里插入图片描述

    • 描述:
      • 按钮想要向Target Servlet发送一个请求 ,但是问题是:他们两个不在一个服务器,属于不同的站点;
      • 需要找一个中间代理人,ProxyServlet向TargetServlet发送请求;
      • 之后,TargetServlet将数据响应给代理人ProxyServlet,然后最终由ProxyServlet将收到的数据响应给前端页面;
      • 实际上,ProxyServlet所做的工作就相当于我们在浏览器上输入地址栏,然后跳转到相对应页面的工作性质是一样的,因为本身响应的数据就是---->所需资源页面的html代码

    代码实现:

    • 前端页面代码:
    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>实现代理解决跨域问题title>
    head>
    <body>
    
    <script type="text/javascript">
      window.onload = () =>{
        document.getElementById("btn").onclick = () =>{
          //创建XMLHttpRequest对象
          var xmlHttpRequest = new XMLHttpRequest();
          //注册回调函数
          xmlHttpRequest.onreadystatechange = () =>{
            if (xmlHttpRequest.readyState == 4) {
              if (xmlHttpRequest.status == 200) {
                document.getElementById("mydiv").innerHTML = xmlHttpRequest.responseText
              }else {
                  alert(xmlHttpRequest.status)
              }
            }
          }
          //打开通道
          xmlHttpRequest.open("GET","/a/proxy",true)
          //发送数据
          xmlHttpRequest.send()
        }
      }
    script>
    
    <button id="btn">使用代理方式解决跨域问题button>
    <div id="mydiv">div>
    body>
    html> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 代理程序:
    @WebServlet("/proxy")
    public class ProxyServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            //通过httpclient组件,发送HTTP Get请求,访问TargetServlet
            // 目标地址
            String url = "http://localhost:8081/b/target";
            HttpGet httpGet = new HttpGet(url);
    
            // 设置类型 "application/x-www-form-urlencoded" "application/json"
            httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
    //        System.out.println("调用URL: " + httpGet.getURI());
    
            //httpClient实例化
            CloseableHttpClient httpClient = HttpClients.createDefault();
            // 执行请求并获取返回
            HttpResponse resp = httpClient.execute(httpGet);
            HttpEntity entity = resp.getEntity();
    //        System.out.println("返回状态码:" + resp.getStatusLine());
            // 显示结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
            String line = null;
            StringBuffer responseSB = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                responseSB.append(line);
            }
            System.out.println("服务器响应的数据:" + responseSB);
            reader.close();
    
            httpClient.close();
    
            response.getWriter().print(responseSB);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 目标程序(这里响应的数据比较简单就是一个json格式的数据):
    @WebServlet("/target")
    public class TargetServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            //相当于跨域的那个服务器,响应一个json格式的字符串
            response.getWriter().print("{\"username\":\"zhangsan\"}");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    Android12 新特性及适配指南
    Golang 动态脚本调研
    CSS 毛玻璃特效运用目录
    K8s Pod 创建埋点处理(Mutating Admission Webhook)
    ​​植物大战僵尸杂交版直装版v2.1 安卓版:全新策略塔防体验
    JS知识点,持续更新
    NLP经典论文研读--transformer-XL论文源码难点记录
    【附源码】Python计算机毕业设计万达影院售票管理系统
    SpringBoot之ElasticsearchRestTemplate常用示例
    2023年11月上旬大模型新动向集锦
  • 原文地址:https://blog.csdn.net/weixin_44606952/article/details/126447837