• Servlet中跨域问题详解


    一、什么是跨域

    跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

    浏览器从一个域名的网页去请求另一个域名的资源时,出现协议、域名、端口、任一不同,都属于跨域。

    比如在百度的一个页面当中,访问京东商城当中的某个资源。这就是跨域。

    跨:跨过。
    域:区域。

    二、环境搭建

    资料地址:

    链接:https://pan.baidu.com/s/1vMserxHqN75EeeL78Sr4WA
    提取码:o5j5

    • 1、新建一个空项目,名称为:跨域Demo
    • 2、建立两个Web项目:A-Model,B-Model
    • 3、对两个框架添加WEB框架支持
    • 4、添加lib依赖:JSP跟Servlet依赖(tomcat下的lib包下就有)
    • 5、为两个模块配置Tomcat,A模块请求路径为:localhost:8080/a   B模块的请求路径为:localhost:8081/b

     

    三、可以跨域的请求

    1、在哪些请求的情况下,跨域没有问题呢?

    1. 超链接
    2. form表单
    3. window.location.href
    4. document.location.href
    5. img: src属性
    6. script: src属性

    只要是以上的请求,跨域都是被允许的。都可以实现。

    2、测试以上的请求是否可以自动跨域访问:

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试哪些请求是可以跨域访问的。</title>
    </head>
    <body>
    
    <!--测试超链接可以跨域吗?可以-->
    <a href="http://localhost:8081/b/index.html">访问b站点当中的index页面</a>
    
    <br><br>
    
    <!--form表单提交可以跨域吗?可以-->
    <form action="http://localhost:8081/b/user/reg" method="post">
        username:<input type="text" name="username"><br>
        password:<input type="password" name="password"><br>
        <input type="submit" value="注册">
    </form>
    
    <!--window.location.href可以跨域吗?可以-->
    <!--document.location.href可以跨域吗?可以-->
    <br>
    <button id="btn1" onclick="window.location.href='http://localhost:8081/b/index.html'">b站点当中的index页面</button><br>
    <button id="btn2" onclick="document.location.href='http://localhost:8081/b/index.html'">b站点当中的index页面</button><br>
    
    <!--使用img可以跨域吗?可以-->
    <img src="http://localhost:8081/b/img/bg_logo.png" width="100px" />
    
    <!--使用script可以跨域吗?可以-->
    <script type="text/javascript" src="http://localhost:8081/b/js/my.js"></script>
    
    </body>
    </html>

    1、超连接

     

    2、form表单

     

    3、测试以下四种的跨域访问

    window.location.href
    document.location.href
    img: src属性
    script: src属性

    四、不可以跨域的请求

    使用XMLHttpRequest对象发送AJAX请求的时候不能跨域!!!

    为什么使用XMLHttpRequest对象发送AJAX请求不能跨域呢?

    • XMLHttpRequest是浏览器内置的对象。(javascript内置的对象)
    • XMLHttpRequest对象一旦创建之后会一直存储在浏览器的内存当中。

    假设你刚刚访问了工行网银的页面,并且在工行网银页面中发送了ajax请求,也就是说创建了XMLHttpRequest对象。

    那么当你没有关闭浏览器的时候,这个对象还在,此时你正好访问了一些其他的不正规的网站,

    如果这些不正规的网站仍然可以无节制的访问工行网银的XMLHttpRequest对象,那将是非常危险的。所以同源策略诞生了。

    1、测试:发送ajax请求的时候,是否有跨域问题!!!

      在a站点当中发送ajax请求。
      访问b站点当中的servlet!!!

    A模块代码:ajax1.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试ajax请求是否可以跨域访问</title>
    </head>
    <body>
    
    <script type="text/javascript">
        // ES6的新特性:箭头函数(不知道的下去可以研究一下。)
        window.onload = () => {
            // 页面加载完毕之后,执行回调函数
            // 给按钮绑定鼠标单击事件
            document.getElementById("btn").onclick = () => {
                // 发送ajax请求
                // 1. 创建ajax核心对象
                let xmlHttpRequest = new XMLHttpRequest(); // let关键字可以声明js变量。和var差不多。都是关键字。但是有区别。
                // 2. 注册回调函数
                xmlHttpRequest.onreadystatechange = () => {
                    /*对象状态值,0—未初始化 1—正在加载  2—加载完毕 3—交互 4—完成。*/
                    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()
            }
        }
    </script>
    
    <!--点击某个按钮发送ajax请求:使用原生的ajax,不使用jQuery-->
    <button id="btn">发送ajax跨域请求</button>
    
    <!--这个div中显示ajax请求响应回来的数据-->
    <div id="mydiv"></div>
    
    </body>
    </html>

    B模块请求:HelloServlet.java

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.getWriter().print("hello ajax!!!!");
        }
    }
    

    测试发送请求

    可以看到下面报错了,错误信息是:Access to XMLHttpRequest at 'http://localhost:8081/b/hello' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

                 翻译:访问位于'http://localhost:8081/b/hello'从原点'http://localhost:8080“”已被CORS策略阻止:请求的资源上不存在“Access Control Allow Origin”标头。


    测试结果:

    使用XMLHttpRequest对象发送ajax请求,进行跨域访问的时候,被“同源策略”阻止。

     2、什么是同源策略

    同源策略是浏览器的一个策略。是一种安全策略。
    也就是说:默认情况下发送ajax请求的时候,只有同源的才能访问,非同源是不允许访问的。
    什么是同源?同源有三要素:协议、域名、端口。
    只有协议、域名、端口完全一致,才是同源。
    以上三要素中任一要素不同,则是非同源。

    3、同源策略带来的问题

    同源策略是解决了安全的问题。
    但是在当下互联网时代,项目的并发量很大,那么项目就一定需要微服务。
    而微服务部署在不同的服务器当中,那么这个时候服务和服务之间调用是非常正常的。
    那这个时候就需要解决这个跨域的问题了。

    五、解决AJAX的XMLHttpRequest对象的跨域问题

     方案一:添加响应头(这是一种真正解决跨域的方案)

    A站点发送ajax请求访问b站点中的一个Servlet,默认情况下因为同源策略的问题,导致不能跨域访问。
    如果B站点中的servlet说了:我允许你访问,你来吧。那就可以了。

    A应用:ajax1.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>测试ajax请求是否可以跨域访问</title>
    </head>
    <body>
    
    <script type="text/javascript">
        // ES6的新特性:箭头函数(不知道的下去可以研究一下。)
        window.onload = () => {
            // 页面加载完毕之后,执行回调函数
            // 给按钮绑定鼠标单击事件
            document.getElementById("btn").onclick = () => {
                // 发送ajax请求
                // 1. 创建ajax核心对象
                let xmlHttpRequest = new XMLHttpRequest(); // let关键字可以声明js变量。和var差不多。都是关键字。但是有区别。
                // 2. 注册回调函数
                xmlHttpRequest.onreadystatechange = () => {
                    /*对象状态值,0—未初始化 1—正在加载  2—加载完毕 3—交互 4—完成。*/
                    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()
            }
        }
    </script>
    
    <!--点击某个按钮发送ajax请求:使用原生的ajax,不使用jQuery-->
    <button id="btn">发送ajax跨域请求</button>
    
    <!--这个div中显示ajax请求响应回来的数据-->
    <div id="mydiv"></div>
    
    </body>
    </html>

    B应用:HelloServlet.java

    package com.bjpowernode.javaweb.servlet;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            // 设置响应头,允许某个,或者某些站点访问
            //response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
            response.getWriter().print("hello ajax!!!!");
        }
    }
    

    测试访问:发现没有出现上面的错误信息,并且能够正常的请求访问

    方案二:JSONP方式

    JSONP: json with padding(带填充的json),这个名字很诡异,后面再说。

    JSONP不是一个真正的ajax请求,或者说他根本就不是一个ajax请求。

    只是一个类ajax请求。可以达到跨域访问,同时又可以达到页面局部刷新的效果。

    jsonp是一种跨域通信的手段,它的原理其实很简单:

    1. 首先是利用script标签的src属性来实现跨域。

    2. 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。

    3. 由于使用script标签的src属性,因此只支持get方法

    1、执行JS代码

    A应用

    <script type="text/javascript" src="http://localhost:8081/b/jsonp1"></script>

    B应用

    package com.bjpowernode.javaweb.servlet;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/jsonp1")
    public class JSONPServlet1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 向前端浏览器输出一端js脚本代码。
            PrintWriter out = response.getWriter();
            // 虽然这里是一个字符串,但是这个字符串输出到前端浏览器之后,前端浏览器的script标签会把他当做一端js代码解释并执行。
            // 表面上看alert(111)是在java程序中调用了前端,其实这是一种错觉。后端只负责相应一段js代码到前端。
            // 真正调用alert(111)代码的还是前端浏览器。
            out.print("alert(111)"); // alert是js内置的一个函数的名字。
        }
    }
    

    执行结果

    2、调用JS函数

    A应用

    <script type="text/javascript">
        function sayHello(){
            alert("我被调用了");
        }
    </script>
    <script type="text/javascript" src="http://localhost:8081/b/jsonp1"></script>

    B应用

    package com.bjpowernode.javaweb.servlet;
    
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/jsonp1")
    public class JSONPServlet1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 向前端浏览器输出一端js脚本代码。
            PrintWriter out = response.getWriter();
            // 这里调用的函数是自定义的函数。
            out.print("sayHello()");
        }
    }
    

    执行结果:

    3、调用JS函数,传入JSON参数

    A应用:

    <script type="text/javascript">
        // 自定义一个函数
        // 不但可以跨域,而且还可以完成前后端的交互。
        function sayHello(data){ // data只是一个变量名,随意的。愿意写什么就写什么。data是一个变量,接受json数据的。
            alert("hello " + data.username)
        }
    </script>
    <script type="text/javascript" src="http://localhost:8081/b/jsonp1?fun=sayHello"></script>

    B应用:

    @WebServlet("/jsonp1")
    public class JSONPServlet1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 向前端浏览器输出一端js脚本代码。
            PrintWriter out = response.getWriter();
            // 调用这个sayHello函数的时候能不能传递一个json对象呢
            // 在java程序中,你看到的是传递一个json格式的字符串。但是到了前端之后,是一个json对象。
            // 将来这里是查询数据库得到的数据,拼接json格式的字符串。
            // 获取函数名
            String fun = request.getParameter("fun");
            out.print(fun + "({\"username\" : \"jackson\"})");
        }
    }

    测试访问:

    4、jsonp解决跨域问题,并添加页面局部刷新效果

    A应用:

    <body>
    <script type="text/javascript">
        // data是json {"username":"zhangsan"}
        function setDiv(data){
            document.getElementById("mydiv").innerHTML = data.username
        }
    </script>
    <script type="text/javascript">
        window.onload = () => {
            document.getElementById("btn").onclick = () => {
                // 拼接scrpit并加载
                // 创建script对象
                const htmlScriptElement = document.createElement("script");
                // 设置script type属性
                htmlScriptElement.type = "text/javascript"
                // 设置src属性
                htmlScriptElement.src = "http://localhost:8081/b/jsonp2?fun=setDiv"
                // 将script标签添加到body当中
                document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)
            }
        }
    </script>
    <button id="btn">jsonp解决跨域问题,页面局部刷新</button>
    <div id="mydiv"></div>
    </body>
    </html>

    B应用:

    @WebServlet("/jsonp2")
    public class JSONPServlet2 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            // 获取函数名
            String fun = request.getParameter("fun");
            // 响应js脚本
            response.getWriter().print(fun + "({\"username\":\"zhangsan\"})");
        }
    }
    

    测试访问:

    方案三:jQuery提供的JSONP方式

    jQuery的JSONP实现方式和方案二一模一样。
    jQuery就是对以上的JSONP进行了封装。
    外面伪装了一个AJAX请求。实际上根本不是AJAX请求。

    A应用:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>jQuery封装的JSONP</title>
    </head>
    <body>
    <!--引入jQuery库-->
    <script type="text/javascript" src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    
    <script type="text/javascript">
        function setDiv(data){
            $("#mydiv").html(data.username)
        }
    </script>
    
    <script type="text/javascript">
        // 这个请求是jQuery底层发送的请求:/b/jsonp2?fun=setDiv&_=1655647292689
        // 添加时间戳是为了解决get缓存问题。
        $(function(){
            $("#btn").click(function (){
                // 发送ajax跨域请求(JSONP方式,底层根本不是ajax请求)
                $.ajax({
                    type : "get",
                    url : "http://localhost:8081/b/jsonp2",
                    dataType : "jsonp", // 数据类型
                    jsonp : "fun", //参数名字
                    jsonpCallback : "setDiv" // 回调函数的名字
                })
            })
        })
    </script>
    
    <button id="btn">jQuery的JSONP实现跨域</button>
    <div id="mydiv"></div>
    
    </body>
    </html>

    B应用:

    @WebServlet("/jsonp2")
    public class JSONPServlet2 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            // 获取函数名
            String fun = request.getParameter("fun");
            // 响应js脚本
            response.getWriter().print(fun + "({\"username\":\"zhangsan\"})");
        }
    }
    

    测试结果:

    方案四:使用代理机制

    在java程序中使用httpclient组件。

    方案五:使用nginx反向代理

  • 相关阅读:
    postman Variables变量的详解与应用
    windows 快速设置环境变量工具 Rapid Environment Editor
    Redis 客户端工具
    python 安装 pymongo
    python ImportError:No module named 'PIL'
    linux 通过命令行终端去控制vnc终端【export DISPLAY使用方法】
    centos7 安装vnc-server 与卸载
    vagrant box centos7硬盘扩容【不删除原数据】
    vboxmanage不是内部或外部命令
    用docker搭建的nginx报upstream错误
  • 原文地址:https://www.cnblogs.com/zhangzhixi/p/16392755.html
Copyright © 2020-2023  润新知