一、跨域:
- 跨域是指从一个域名的网页去请求另一个域名的资源。比如从百度(https://baidu.com)页面去请求京东(https://www.jd.com)的资源。
- 通过超链接或者form表单提交或者超链接的方式进行跨域是不存在问题的。但在一个域名的网页中的一段js代码发送ajax请求去访问另一个域名中的资源,由于同源策略的存在导致无法跨域访问,那么ajax就存在这种跨域问题。
- 有一些情况下,我们是需要使用ajax进行跨域访问的。比如某公司的A页面(8080:a.bjpowernode.com)有可能需要获取B页面(8081:b.bjpowernode.com)。
二、同源策略:
- 同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,同源就是协议(http)、域名(localhost)和端口(8080)都相同。
- Ajax无法跨域:http://localhost:8080/这个域名中的文件不能访问http://localhost:8081/这个域名中的内容。但是form表单和超链接可以。
- 同源策略有什么用?如果你刚刚在网银输入账号密码,查看了自己还有1万块钱,紧接着访问一些非法的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。所以,从安全的角度来讲,同源策略是有利于保护网站信息的。
三、解决Ajax跨域问题的方案:
方案一:设置响应头
在被访问的servlet中加入下述代码表示允许该域名访问我(该servlet)。
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允许某个域名访问我
response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有域名访问我
方案二:jsonp
- jsonp:json with padding(带填充的json)
- jsonp不是ajax请求,但是可以完成局部刷新的效果,并且可以解决跨域问题。
- 注意:jsonp解决跨域的时候,只支持GET请求。不支持post请求。
- 原理:script标签的src属性值可以是URL,作用是访问某个servlet,后端servlet响应该请求并返回一个字符串到标签中,浏览器接收到这个字符串后,会自动将字符串当做JS代码执行。
如果后端响应的字符串是一段JS代码的字符串,那么就能实现JS的一系列操作,例如使用innerHTML方法动态生成标签的值、调用前端创建的函数等,类似ajax异步刷新。 - 例子:用户点击按钮后从后端接收数据展示到前端。这里后端接收的数据是一个前端早已写好的函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp跨域问题</title>
</head>
<body>
<script type="text/javascript">
function dosome(myjson){//这是我们自己写的函数,一会后端返回的JS代码是fun(json),然后调用我们这个函数给div标签内容赋值
document.getElementById("mydiv").innerHTML = myjson.name;
}
window.onload =() => {//箭头函数
document.getElementById("btn").onclick = () =>{//箭头函数,点击按钮创建一个<script>标签
var myscript = document.createElement("script");//创建元素对象
myscript.type = "text/javascript";//给对象设置type属性
myscript.src = "http://localhost:8081/a/servlet01?fun=dosome";//给对象设置src属性,这里要加fun=dosome,这是get属性向后端传送数据的方法,目的是告诉后端我们自己写的函数名,方便后端使用我们前端自己写的函数
document.getElementsByTagName("body")[0].appendChild(myscript);//将script标签添加到body中
}
}
</script>
<button id="btn">解决ajax跨域问题</button>
<div id="mydiv"></div>
</body>
</html>
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet01")
public class Servlets extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
//获取前端发送过来的函数名
String fun = request.getParameter("fun");
response.getWriter().print(fun+"({\"name\":\"zhangsan\"})");//返回的事一个JS代码:fun(json对象)
}
}
方案三:jQuery封装jsonp
- jQuery中的jsonp其实就是我们方案2的高度封装,底层原理完全相同。
- 核心代码:
//看似是ajax请求,实际上不是ajax请求。 $.ajax({ type : "GET", url : "跨域的url",//不需要传函数名了,函数名默认传的是&jsonp属性的值=jsonpCallback属性的值。 dataType : "jsonp", // 指定数据类型,这里是重点!!区分ajax和jsonp的依据 jsonp : "fun", // 指定参数名(不设置的时候,默认是:"callback") jsonpCallback : "sayHello" // 这里的函数就是我们自定义的函数 // (不设置的时候,jQuery会自动生成一个随机的回调函数, //并且这个回调函数还会自动调用success的回调函数。所以如果不设置这个属性则必须设置success属性) })
方案四:代理机制(httpclient)
- 我们知道1号服务器不能使用异步ajax直接访问二号服务器,所以我们可以1号服务器访问本服务器的servlet,然后本服务器的servlet去访问2号服务器的servlet。
- 使用Java程序怎么去发送get/post请求呢?【GET和POST请求就是HTTP请求。】
- 第一种方案:使用JDK内置的API(java.net.URL…),这些API是可以发送HTTP请求的。
- 第二种方案:使用第三方的开源组件,比如:apache的httpclient组件。(httpclient组件是开源免费的,可以直接用)
- 在java程序中,使用httpclient组件可以发送http请求。
- 对于httpclient组件的代码,大家目前可以不进行深入的研究,可以从网上直接搜。然后粘贴过来,改一改,看看能不能完成发送get和post请求。
- 使用httpclient组件,需要先将这个组件相关的jar包引入到项目当中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>代理跨域问题</title>
</head>
<body>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(function () {
$("#btn").click(function () {
$.ajax({
type : "get",
url : "/b/zhuan",
async : true,
dataType:"json",
success : function(json) {
$("#mydiv").html(json.name);
}
})
})
})
</script>
<button id="btn">解决ajax跨域问题</button>
<div id="mydiv"></div>
</body>
</html>
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@WebServlet("/zhuan")
public class Zhongzhuan extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 使用java代码去发送HTTP get请求
// 目标地址
//String url = "https://www.baidu.com";
String url = "http://localhost:8081/a/servlet02";
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 response1 = httpClient.execute(httpGet);
HttpEntity entity = response1.getEntity();
//System.out.println("返回状态码:" + response.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);
}
response.getWriter().print(responseSB);
//System.out.println("服务器响应的数据:" + responseSB);
reader.close();
httpClient.close();
}
}
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet02")
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.getWriter().print("{\"name\":\"zhangsan\"}");//返回的事一个JS代码:fun(json对象)
}
}
方案五:nginx反向代理
- nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改一个nginx的配置即可。具体细节老师没讲。