跨域请求
跨域请求 Cross-Origin Request:指从一个域名的网页去请求另一个域名的资源。两个请求不是同源的,那这两个请求就视为跨域请求。
同源:协议、IP(域名)、端口号都一致,则同源。
- https协议与http协议不同源
- uri不作为同源的判断标准
在HTTP中,为保护网站和用户信息安全,默认情况下是浏览器是不支持跨域请求的,不安全的跨域请求的响应报文会被浏览器丢弃,并给出控制台错误信息。这被称为“同源策略 Same-origin policy“。


- AJAX跨域请求正常发送,响应200。响应头中没有给出CORS信息,浏览器认为此请求是不安全的,丢弃响应报文(页面无变化),控制台给出错误信息。
- 跨域请求是照发的,响应也是照收的,丢弃还是提交由浏览器说了算。HBuilder内置浏览器就没有同源策略,所有响应都提交。
<script>的scr属性、<a>的href属性是可以跨域的。
超链接点击后是一个传统的GET请求,不存在跨域的安全性问题,自然不受同源策略的影响。
<script>给出src属性后,标签体的内容会被忽略,同时向src指定的URL/URI发送GET请求,将响应体作为标签体解析。对于
<script src="..." type="text/javascript">,响应体的内容会被当作js代码执行。? 通过
<script>可以实现XSS攻击,但前提是用户主动操作dom增加<script>元素。
解决AJAX跨域:想要进行跨域请求,需要使用特定的方式来处理,如 JSONP、CORS、postMessage 等跨域技术。
CORS
CORS(Cross-Origin Resource Sharing):跨域资源共享,是一种安全机制,能让 Web 应用服务器能够访问不同源的资源。
CORS 机制使用特殊的 HTTP 响应头来告诉浏览器Web 服务器允许的跨源访问范围(发起的请求落入这个范围则视为安全的跨域访问,浏览器对比发起的请求,从而判断是否提交响应报文),从而实现前端页面和后端服务器的跨域访问功能。
CORS实现只需要在响应头中添加特定信息即可:
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允许特定源的跨域访问
response.setHeader("Access-Control-Allow-Origin", "*");
// 允许所有源的跨域访问
// 只支持如上两种方式,如:http://localhost:* 是错误的
浏览器检查响应头Access-Control-Allow-Origin,判断是否提交响应报文。
JSONP
JSONP(JSON with Padding),又称“JSON-P”或“JSON/P”,是一种跨域数据交互的解决方案。它实际上是一种“协议”,而不是一种数据格式。该协议允许在不违反跨域安全限制的情况下,从不同域名的服务器上获取JSON数据。
JSONP可以通过动态生成<script>标签来请求外部数据,并通过回调函数来实现数据的异步载入和处理。JSONP通常被用于跨域数据的传输,常见于API接口的调用。
JSONP是一种类AJAX的机制,也可以实现局部刷新,利用<script>标签的src属性发起GET请求(只支持GET请求),要求响应内容以调用JS函数的形式返回。
原生态实现JSONP
JSONP的实现需要前后端进行配合,后端需要将原响应内容进一步封装成对某个js函数的调用,并将原响应内容作为调用的实参。
前端:
<!--函数的创建要先于JSONP的发起-->
<script type="text/javascript">
// 自定义的函数,函数体中完成对原响应体的处理
function sayHello(data){ // data是一个json:{"username" : "lucy"}
document.getElementById("mydiv").innerHTML = data.username
}
</script>
<!--浏览器加载脚本块时发起JSONP-->
<script type="text/javascript" src="http://localhost:8081/b/jsonp2?fun=sayHello"></script>
<!--注册事件发起JSONP-->
<script type="text/javascript">
window.onload = () => {
document.getElementById("btn").onclick = () => {
// 加载script元素
// 创建script元素对象
const htmlScriptElement = document.createElement("script");
// 设置script的type属性
htmlScriptElement.type = "text/javascript"
// 设置script的src属性
htmlScriptElement.src = "http://localhost:8081/b/jsonp2?fun=sayHello"
// 将script对象添加到body标签中(这一步就是加载script)
document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)
}
}
</script>后端:
- 可以后端直接封装对一个特定的js函数(响应内容作为参数)的调用作为返回:
out.print("sayHello( {'name' : 'jackson'} )"); // {'name' : 'jackson'}是原响应体- 更多的情况下,是JSONP发起的GET请求携带一个函数句柄参数,后端获得这个函数句柄,再封装调用作为返回:
// 请求:URL?fun=sayHello
// 动态获取函数名
String fun = request.getParameter("fun");
out.print(fun + "( {'name' : 'jackson'} )"); // 拼接为 "sayHello( {'name' : 'jackson'} )"使用jQuery实现JSONP
底层原理完全相同,仅仅是通过jQuery的封装完成请求的发送和响应结果的处理。
$.ajax({
type : "GET",
url : "跨域的url",
dataType : "jsonp", // 指定数据类型
jsonp : "fun", // 指定参数名(不设置的时候,默认是:"callback")
jsonpCallback : "sayHello" // 指定回调函数的名字
// (不设置的时候,jQuery会自动生成一个随机的回调函数,
// 并且这个回调函数还会自动调用success的回调函数。)
})正向代理
正向代理(Forward Proxy):代理服务器代表客户端向服务器请求资源。
在正向代理中,代理服务器隐藏了客户端的真实IP地址,代替客户端访问网络资源。常见的例子是科学上网工具,使用代理服务器访问被封锁的网站。
使用JDK内置API完成代理请求的发送
较为复杂。
使用Apache的HttpClient组件
HttpClient是一个用于发送HTTP请求和接收响应的Java类库,它是Apache Software Foundation在Jakarta项目下的子项目之一。
它可以模拟浏览器的行为,支持GET、POST、PUT、DELETE等多种请求方式,同时还支持HTTPS和文件上传等功能。HttpClient可以用于编写网络爬虫、Web程序、测试工具等多种应用场景。
// 使用java代码去发送HTTP get请求
// 目标地址
//String url = "https://www.baidu.com";
String url = "http://localhost:8081/b/hello";
HttpGet httpGet = new HttpGet(url); // 封装请求信息
// 设置类型 "application/x-www-form-urlencoded" "application/json"
httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
// <form enctype="application/x-www-form-urlencoded"></form>
// httpClient实例化(创建*代理对象)
CloseableHttpClient httpClient = HttpClients.createDefault();
// 通过代理对象发送请求并获取返回
HttpResponse response = httpClient.execute(httpGet);
// 从相应报文中解析响应体
HttpEntity entity = response.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);
}
System.out.println("服务器响应的数据:" + responseSB);
reader.close(); // 关闭流
httpClient.close(); // 关闭代理连接nginx反向代理
反向代理(Reverse Proxy):代理服务器代表服务器向客户端提供服务。
在反向代理中,代理服务器隐藏了服务器的真实IP地址和内部网络结构,代替服务器响应客户端的请求。常见的例子是网站负载均衡,由反向代理服务器将客户端请求分配给多个服务器,以提高网站的可用性和性能。
修改nginx配置即可。
