Ajax——AJAX跨域问题

发布于:2023-01-09 ⋅ 阅读:(389) ⋅ 点赞:(0)

1. 跨域问题描述

1.1 跨域的含义?

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

1.2 哪些访问存在跨域问题

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

2. 同源问题

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

2.1 同源策略作用

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

2.2 同源判断

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

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

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

3. 解决跨域问题

说明:

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

3.1 方案一:设置响应头

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

模拟实现核心代码:

// 使用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()
    }
}

同源

  • 响应给前端数据
@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\"}");
    }
}

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>
  • 响应数据
String fun = request.getParameter("fun");

response.getWriter().print(fun+"({\"name\":\"json\"})");

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库封装的jsonp</button>
<div id="mydiv"></div>
</body>
</html>
  • 后端响应
@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\"})");
    }
}

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> 
  • 代理程序:
@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);
    }
}
  • 目标程序(这里响应的数据比较简单就是一个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\"}");
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到