本来想说是深入研究一下,但是水平有限,也就是手放在水面往下多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服务器去转发。