闭关修炼(二十四)浅入了解跨域问题

本来想说是深入研究一下,但是水平有限,也就是手放在水面往下多0.25厘米的深度的样子
,掠于皮毛。



什么是跨域问题

跨域其实是浏览器的一个安全机制,请求访问域名与AJAX请求地址不一致,浏览器会无法返回请求结果。

具体的例子,现在我在浏览器访问url地址www.aaa.com/a,而这个页面后台服务器又发送请求给www.bbbb.com/b,因为浏览器跨域安全机制,阻止你的访问。

跨域问题模拟

我们先配置一下host,添加两个自定义的域名便于我们测试
在这里插入图片描述

a web项目创建一个测试servlet,把tomcat启动起来

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("name");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);
        resp.getWriter().write(jsonObject.toJSONString());
    }
}

b web项目的index.jsp访问a的测试接口

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>

  <script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>

  <script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "GET",
        async : false,
        url : "http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123",
        dataType : "json",
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>
  <body>
  </body>
</html>

访问b web项目的index.jsp,就会报跨域错误
在这里插入图片描述
在这里插入图片描述
这是因为AJAX请求的域名和我当前访问的域名是不一致的,浏览器默认不会获取ajax结果。

浏览器默认网站ajax请求是在同一个域名下的。这是浏览器的机制。

跨域问题如何解决

使用jsonP 只支持get请求,不支持post

使用接口网关 ,springcloud (企业开发常用)

httpclient 内部转发

response添加header请求头允许跨域。

但是我水平有限,springcloud先放一放

先说一下几种方法

response添加请求头解决跨域问题

在a web项目中加多一段代码

resp.setHeader("Access-Control-Allow-Origin", "*");

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);
        resp.getWriter().write(jsonObject.toJSONString());
        // 允许浏览器跨域访问,*允许所有
        resp.setHeader("Access-Control-Allow-Origin", "*");
    }
}

b网站重新访问,正常返回结果
在这里插入图片描述

jsonp解决跨域问题

JSONP使用在前端中的

JSON只支持GET,也不怎么好用

修改ajax代码,dataType改为jsonp,加一行jsonp: “jsonpCallback”

<script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "GET",
        async : false,
        url : "http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123",
        dataType : "jsonp",
        jsonp: "jsonpCallback",// 服务器端用于接收回调的func参数
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>

服务器端修改,获取jsonpCallback参数,构造String返回结果,jsonpCallback + ( + 返回内容 + )

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);

        String jsonpCallback = req.getParameter("jsonpCallback");
        System.out.println(jsonpCallback);

        resp.getWriter().println(
                String
                .format("%s(%s)",
                        jsonpCallback,
                        jsonObject.toJSONString()));

        //resp.setHeader("Access-Control-Allow-Origin", "*");
    }
}

查看结果,访问url后面带有jsonpCallback参数,这个参数是客户端生成出来的,访问的时候jsonpCallback的值帮我们自动构造了。对应jsonp: “jsonpCallback”
在这里插入图片描述
响应结果
在这里插入图片描述
在这里插入图片描述
原理是什么呢?

使用script发送get请求,我们在引用jquery.min.js的时候用的就是script请求,它是能跨越获取结果的,jsonp用这个这个方法发送请求,回调参数,将参数解析,获取取结果。

这里参数回调结果是
jsonpCallback=jQuery17206283321772985047_1611579842743&_=1611579842749

解析参数,就取得了服务器发送的结果
&_=1611579842749 -> username: “123”

注意类型是script类型的
在这里插入图片描述

jsonp是不支持post的,我们可以测试一下

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("调用了GET");
        //String username = req.getParameter("username");
        //JSONObject jsonObject = new JSONObject();
        //jsonObject.put("username", username);
        //
        //String jsonpCallback = req.getParameter("jsonpCallback");
        //System.out.println(jsonpCallback);
        //
        //resp.getWriter().write(
        //        String
        //        .format("%s(%s)",
        //                jsonpCallback,
        //                jsonObject.toJSONString()));

        //resp.setHeader("Access-Control-Allow-Origin", "*");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("调用了POST");
        String username = req.getParameter("username");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);

        String jsonpCallback = req.getParameter("jsonpCallback");
        System.out.println(jsonpCallback);

        resp.getWriter().write(
                String
                        .format("%s(%s)",
                                jsonpCallback,
                                jsonObject.toJSONString()));
    }
}

把前端代码改成POST请求,访问测试,alert了fail,但是返回200,请求方式自动变成了GET
在这里插入图片描述

代码明明请求方式写的是post的
在这里插入图片描述
服务器后台打印,完全没有走post的方法
在这里插入图片描述
所以jsonp不支持post

httpclient内部转发解决跨域问题

b项目内部发送httpclient给a项目

b项目引入httpclient jar包

		<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>

写httpclient转发

@WebServlet("/ForwardToAServerServlet")
public class ForwardToAServerServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建默认连接
        CloseableHttpClient httpClient = HttpClients.createDefault();

        // 创建get连接
        HttpGet httpGet = new HttpGet("http://a.a.com:8080/biguanxiulian_2_war_exploded/test?username=123");
        // 执行访问
        CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpGet);

        // 获取状态码
        int code = closeableHttpResponse.getStatusLine().getStatusCode();

        // 获取状态
        System.out.println("code:" + code);

        if (code == 200) {
            resp.getWriter().write(
                    EntityUtils
                            .toString(closeableHttpResponse.getEntity()));
        }

        // 关闭连接
        closeableHttpResponse.close();
        httpClient.close();
    }
}

访问ForwardToAServerServlet接口,是经由httpclient调用接口获取的结果
在这里插入图片描述
那修改index.jsp,访问自己的ForwardToAServerServlet接口就行了,成功的绕过了浏览器的跨域检测

<html>
  <head>
    <title>$Title$</title>
  </head>

  <script src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>

  <script type="text/javascript">
    $(document).ready(function (){
      $.ajax({
        type : "get",
        async : false,
        url : "http://b.b.com:8081/javaWEBquick_war_exploded//ForwardToAServerServlet",
        dataType : "json",
        //jsonp: "jsonpCallback",// 服务器端用于接收回调的func参数
        success : function (data){
          alert(data["username"])
        },
        error : function (){
          alert("fail")
        }
      })
    })
  </script>
  <body>
  </body>
</html>

成功解决跨域问题
在这里插入图片描述
使用httpclient的缺点是效率点,有点类似重定向,做了两次请求。

这种方法最大好处是十分安全,抓包无法分析你真正调用的接口,访问接口的请求隐藏在了httpclient转发中,类似加了层壳。


写在后面

其实在项目中都不用这些办法解决跨域问题。

小demo中快速解决跨域,可以用用。

项目一般使用网关接口,搭建api接口网关,那么原理是什么呢?

服务器搭建nginx,服务器部署了多个不同的项目,客户端访问nginx服务器,nginx判断项目名称转发客户端的访问。

这样做的好处是:客户端都访问一个域名,而跨域的工作交给nginx服务器去转发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值