文章目录
- 跨域
 - 
- 哪些方式可以进行跨域
 - 
- 部署服务器
 - 部署模块
 - 
- ajax1
 - ajax2
 
 - 测试
 
 
 - 跨域解决方案
 - 
- 方案1:设置响应头
 - 方案2:jsonp
 - 
- 深入一下jsonp
 
 - 方案3:代理机制(httpclient)
 - 
- 第一种方案:使用JDK内置的API
 - 第二种方案:使用第三方的开源组件
 
 - 方案4:nginx反向代理
 
 
跨域
跨域是指从一个域名的网页去请求另一个域名的资源。
 例如从百度页面点击超链接请求京东的资源。
哪些方式可以进行跨域
我们来测试一下
 包括超链接、form表单、JS代码、跨域<script>标签加载JS代码、跨域加载图片、以及AJAX请求
部署服务器
首先建两个服务器,服务器1:HTTP为8080端口,JMX端口为1099,部署ajax1模块,服务器2:HTTP为8081端口,JMX端口为1098,部署ajax2模块
 
 服务器1:
 
 服务器2:
 
 
部署模块
然后ajax1去跨域请求ajax2
ajax1
ajax1 html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax1</title>
</head>
<body>
<!--测试超链接跨域-->
<a href="http://localhost:8081/ajax2">ajax2跨域请求</a><br>
<!--测试form表单跨域-->
<form action="http://localhost:8081/ajax2/user" method="post">
    username:<input type="text" name="username"/><br>
    password:<input type="password" name="password"/><br>
    <input type="submit" value="form跨域"><br>
</form>
<!--测试js代码的跨域-->
<hr>
<button onclick="window.location.href='http://localhost:8081/ajax2'">JS跨域请求</button><br>
<button onclick="document.location.href='http://localhost:8081/ajax2'">JS跨域请求</button><br>
<!--测试使用script跨域加载-->
<script type="text/javascript" src="http://localhost:8081/ajax2/my.js"></script>
<h3>测试跨域加载图片</h3>
<img src="http://localhost:8081/ajax2/img/2.jpg" style="width: 100px">
<hr>
<!--发送AJAX跨域请求,响应到div里面-->
<script type="text/javascript">
    window.onload = function (){
        document.getElementById("btn").onclick = function (){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function (){
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        document.getElementById("mydiv").innerHTML = this.responseText
                    }
                }
            }
            xhr.open("GET","http://localhost:8081/ajax2/hello",true)
            xhr.send()
        }
    }
</script>
<button id="btn">AJAX跨域请求</button>
<div id="mydiv"></div>
</body>
</html>
ajax2
在ajax2模块,导入图片,以及js代码测试
 html页面
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax2</title>
</head>
<body>
<h1>ajax2页面</h1>
</body>
</html>
js代码
alert("js加载了")//页面加载就会弹出
UserServlet用于测试form表单
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        out.print(username + "," + password);
    }
}
HelloServlet用于测试ajax请求
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().print("Hello!!!!!");//简单响应
    }
}
测试
- 1.测试超链接
 
点击超链接,成功跳转
 
- 2.测试form表单
 
输入信息,点击提交,发现成功跳转显示
 
 
- 3.测试JS代码
 
点击两个按钮分别是用window和document,发现都成功
 
- 4.测试跨域加载ajax2的JS代码
 
正常应该是页面没出来就加载了,发现确实加载了
 
- 5.测试跨域加载图片
 
刷新页面,图片加载
 
 6.测试ajax跨域请求
点击ajax请求按钮,发现没动静打开F12,发现请求是发过去了
 

 
但是报错误:
Access to XMLHttpRequest at 'http://localhost:8081/ajax2/hello' from origin
'http://localhost:8080' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
意思是这个请求被CORS策略阻止,并且请求源上不存在“Access Control Allow Origin”标头。
 CORS策略阻止:表示这个ajax请求被同源策略阻止
 根本原因:在跨域的时候不允许出现XMLHttpRequest对象,因为共享同一个XMLHttpRequest对象是不安全的
 同源策略:是浏览器的一种安全策略,指一段脚本只能读取来自同一来源的窗口和文档的属性,同源就是协议、域名和端口都相同。
 例如:如果你刚刚在某网银输入账号密码,查看了自己还有账户金额,紧接着又访问一些不规矩的网站,
 这些网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。所以,从安全的角度来讲,同源策略是有利于保护网站信息的。
 同源的三要素
- 协议
 - 域名
 - 端口
 
只有三个要素同时一致,才是同源,否则为不同源
跨域解决方案
现在大部分系统都是大型应用,分布式的微服务的,有一些情况我们是需要使用ajax进行跨域访问的,所以就需要解决跨域的问题
方案1:设置响应头
来源于报错:请求源上不存在“Access Control Allow Origin”标头。
 核心原理:跨域访问里的资源允许你跨域访问。
 有两种设置方式
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允许某个,这里允许http://localhost:8080这个源,也就是允许ajax1访问
response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有
在HelloServlet里设置之后发现响应回来了
 
方案2:jsonp
来源于<script>能够跨域加载JS代码,那能不能加载Servlet呢
 在ajax1模块新建个html页面测试一下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp实现跨域</title>
</head>
<body>
<script type="text/javascript" src="http://localhost:8081/ajax2/jsonp"></script>
</body>
</html>
ajax2新建个Servlet
@WebServlet("/jsonp")
public class JsonpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.print("alert('111')");//响应一段JS代码
    }
}
结果:
 
 发现<script>是可以加载Servlet的,也就是说到时候响应一段字符串拼接的调用函数,参数是json类型,到时候这个json就是一个业务结果,传过来,让前端调用
 例如
 html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp实现跨域</title>
</head>
<body>
<script type="text/javascript">
    function sayHello(data){
        alert("hello:" + data.name)
    }
</script>
<script type="text/javascript" src="http://localhost:8081/ajax2/jsonp?fun=sayHello"></script>
</body>
</html>
ajax2响应带json的JS代码
@WebServlet("/jsonp")
public class JsonpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fun = request.getParameter("fun");
        PrintWriter out = response.getWriter();
        out.print(fun + "({\"name\":\"zhangsan\"})");
    }
}
结果:
 
jsonp:json with padding(带填充的json)
- jsonp不是一个真正的ajax请求。只不过可以完成ajax的局部刷新效果。可以说jsonp是一种类似ajax请求的机制。
 - 注意:jsonp解决跨域的时候,只支持GET请求。不支持post请求。
 
深入一下jsonp
我们不希望是页面加载的时候执行,我们需要用户点击按钮的时候刷新局部,怎么办?
 我们需要在用户点击按钮的时候把
<script type="text/javascript" src="http://localhost:8081/ajax2/jsonp?fun=sayHello"></script>
这个内容加载到body标签里面
 实现
 ajax1模块html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp点击按钮跨域</title>
</head>
<body>
<script type="text/javascript">
    function sayHello(data){
        document.getElementById("mydiv").innerHTML = data.name
    }
    window.onload = function (){
        document.getElementById("btn").onclick = function (){
            //创建script
            const htmlScriptElement = document.createElement("script");//创建script元素
            //设置属性script
            htmlScriptElement.type = "text/javascript"
            htmlScriptElement.src = "http://localhost:8081/ajax2/jsonp?fun=sayHello"
            document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)//将script设置到body里
        }
    }
</script>
<button id="btn">jsonp局部刷新</button>
<div id="mydiv"></div>
</body>
</html>
ajax2模块后端代码
@WebServlet("/jsonp")
public class JsonpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	//一般这里要连接数据库,这里就不连接了
        String fun = request.getParameter("fun");
        PrintWriter out = response.getWriter();
        out.print(fun + "({\"name\":\"zhangsan\"})");
    }
}
结果:点击按钮,发现ajax2响应了一段JS代码
 
 牛人们写的jQuery库,已经对jsonp进行了封装。大家可以直接拿来用。
 用之前需要引入jQuery库的js文件。
 jQuery中的jsonp其实就是我们方案2的高度封装,底层原理完全相同。
 核心代码
$.ajax({
    type : "GET",
    url : "跨域的url",
    dataType : "jsonp", // 指定数据类型
    jsonp : "fun", // 指定参数名(不设置的时候,默认是:"callback")
    jsonpCallback : "sayHello" // 指定回调函数的名字
							   // (不设置的时候,jQuery会自动生成一个随机的回调函数,
    						   //并且这个回调函数还会自动调用success的回调函数。)
})
方案3:代理机制(httpclient)
代理跨域就是当用户点击按钮的时候,发送不跨域的ajax请求,让后端ProxyServlet代理,使用java代码代理发送跨域请求,然后服务器2TargetServlet响应回来的数据是传输给ProxyServlet,通过ProxyServlet代理转响应到前端,避开了跨域。
ajax请求:服务器1按钮->服务器1后端ProxyServlet代理->服务器2TargetServlet
响应:服务器2TargetServlet->服务器1ProxyServlet代理->服务器1前端
Java程序怎么去发送get/post请求呢?
第一种方案:使用JDK内置的API
使用JDK内置的API(java.net.URL…),这些API是可以发送HTTP请求的。
第二种方案:使用第三方的开源组件
比如:apache的httpclient组件。(httpclient组件是开源免费的,可以直接用)
方案4:nginx反向代理
nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改一个nginx的配置即可。