1 什么是跨域
就是A服务器去请问访问B服务器中的资源。
1.1 跨域访问的测试
1.1.1 测试1:同一个服务器中(jt-manage)中的测试
浏览器要是想通过jt-manage服务器去访问jt-manage项目中的test.html,通过客户端发送ajax请求(请求test.json)。
jt-manage服务器可以给客户端返回test.json中“name”对应的value:tom
这个结果不会被浏览器拦截掉。客户端可以正常显示结果。
test.html和test.json 这两个文件都在jt-manage中的webapp目录下。
说明:
- 浏览器中输入的网址url: http://manage.jt.com/test.html
- ajax请求中的地址url: http://manage.jt.com/test.json
通过对比这两个url发现: 请求协议名称:// 域名 :端口号 这3项都相同时,客户端能正常收到服务器响应回来的数据,并展现出来。
1.1.2 测试2:在不同的服务器中(jt-web和jt-manage)之间的测试
浏览器要是想通过jt-web服务器中的test.html,去访问jt-manage中的test.json文件,通过客户端发送ajax请求(请求test.json)。
jt-manage服务器给客户端返回test.json中“name”对应的value:tom
这个结果会被浏览器拦截掉(因为浏览器发现,ajax请求是jt-web的前端发出去的,得到的结果却是jt-manage返回的,这是不可以的)。jt-web的客户端不会显示结果。
说明:
- 浏览器中输入的网址url: http://www.jt.com/test.html
- ajax请求中的地址url: http://manage.jt.com/test.json
通过对比这两个url发现: 这两个url的域名不相同,客户端没有正常显示结果数据。
1.2 同源策略介绍
同源策略规定:
浏览器在解析客户端发送的ajax请求时,如果发现ajax请求的协议名称://请求的域名:请求的端口号与 浏览器网址中输入的地址都相同时,浏览器可以正确的解析返回值.从而客户端能成功展现服务器的响应数据。
客户端发送的这个访问叫同域访问。
跨域访问:
但是如果违反了同源策略中的任意一条,则叫做跨域访问.
浏览器出于安全性的考虑.不予解析返回值(请求会被服务器正常处理,但是客户端接收不到返回值).
易错点:
http://localhost:8091/text.html 与
http://manage.jt.com/text.html
属于跨域访问(域名和端口号都不同):
nginx是将localhost 映射到 manage.jt.com上的,它俩不能看做是同一个域名。
并且manage.jt.com的端口号后面是80(nginx默认的端口号)。
1.3 实现跨域的方式一:通过JSONP
JSONP,全称:JSON with Padding 。它是json的一种使用模式,(意思就是它有自己独特的处理json串的方法。)
1.3.1 JSONP实现跨域的原理(步骤)
通常情况下,A服务器是无法得到B服务器的返回值的。
但A服务器如果用了JSONP方式,就能访问B服务器。它是怎么做到的呢?
主要原因:
A服务器中的.html文件中,script标签中,多配置了src这么一个属性
src属性不受同源策略的约束.可以获取B服务器数据.
也就是说,
第一步,要在A服务器的html文件(浏览器url后面接的是哪个html,就在A服务器中找哪个html)中的script标签中,添加src属性,src后面跟的就是想要访问的B服务器的文件的地址。
第二步,自定义回调函数的名称,可以任意起名
在A服务器的html文件中,
第三步,将B服务器中的返回的json串,进行特殊的封装。
即把json串用我上一步自定义的回调函数的名称包裹。
在B服务器中找到要返回的json串结果。
1.3.2 JSONP跨域方式的优化
1.3.1中的3大步骤是jsonp实现跨域的根本。
但这个方法比较麻烦,待优化的2个点:
1:A服务器中
这种将请求写在src中,非常不方便操作,能否封装为ajax调用方式?
2:B服务器中这种将回调函数的名字写死,也是不好的。怎么才能动态获取呢?
其实:在浏览器中输入的地址一般都是这样的:
后面的jQuery111…都是回调函数的名字,前面的callback是接收回调函数名字的一个变量。
所以,将在B服务器中需要写回调函数名字的地方,就把callback这个变量传过去就行了。
1.3.3 JSONP:通过ajax的形式发送请求
<script type="text/javascript">
$(function(){
alert("测试访问开始!!!!!")
$.ajax({
url:"http://manage.jt.com/web/testJSONP",
type:"get", //jsonp只能支持get请求 src只能是get请求
dataType:"jsonp", //dataType表示返回值类型 (平时都是json,可以省略不写)
jsonp: "callback", //指定接收回调函数的变量的名称 通常都写callback
jsonpCallback: "hello", //指定回调函数名称 这样浏览器中的回调函数名后面就不会跟一长串数字了
success:function (data){ //data经过jQuery封装返回就是json串
alert(data.itemId);
alert(data.itemDesc);
}
});
})
</script>
1.3.4 编辑jt-manage中UserController,用以返回数据
既然jt-web人家发起请求要访问jt-manage中的资源,那jt-manage中就要有一个Controller来接这个请求。
在jt-manage中新建一个UserController
@RestController
public class JSONPController {
@RequestMapping("/web/testJSONP")
public String jsonp(String callback){ //callback是个变量 由客户端发ajax请求时,传来,比如可以是hello 可以是abc....
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(888L).setItemDesc("封装后的跨域测试~~~~");
//我自己手动将itemDesc对象转换为json字符串
String json = ObjectMapperUtil.toJSON(itemDesc);
return callback+"("+json+")"; //JSONP中规定返回的json串要放在回调函数中
}
}
1.3.5 通过JSONObject对象(SpringBoot提供的),简化我手动转化json字符串的操作
@RestController
public class JSONPController {
/**
* 测试用jquery封装后的跨域请求,能否成功
* url地址: http://manage.jt.com/web/testJSONP?callback=jQuery1111008886989701217463_1600139002367&_=1600139002368
* 返回值的类型应该是经过特殊格式封装的json数据 : callback(json字符串)
* 也是一种字符串,所以jsonp方法的返回值那才能写String
* SpringBoot中为了方便我的跨域的操作,就给我提供了一个API对象JSONPObject,它可以自动帮我把回调函数名和返回的结果拼接起来。就不用我自己手动拼接了。
*/
@RequestMapping("/web/testJSONP")
public JSONPObject jsonp(String callback){ //callback是个变量 比如可以是hello 可以是abc....
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(888L).setItemDesc("用SpringBoot中提供的API封装后的跨域测试~~~~");
//通过JSONPObject拼接回调函数名和服务器返回的结果
JSONPObject jsonpObject = new JSONPObject(callback, itemDesc);
return jsonpObject;
}
}
1.3.6 关于ajax请求时间毫秒数的说明
说明:
由于浏览器内部有缓存机制,所以如果遇到了一个相同的请求地址(即回调函数名相同了),浏览器可能会展现的是之前的结果(即使用缓存),但是有些数据必须要求浏览器重数据库中动态获取数据,为了避免这种情况,一般在url最后添加随机数或者时间毫秒数区分url请求(区分回调函数名)
1.4 实现跨域的方式二:通过CORS
CORS实现跨域的原理与JSONP完全不同。
它是让B服务器给返回的数据(Response对象)上进行一些标识,浏览器看到这些标识,就不再拦截了,A服务器的客户端就接收到了这些数据。
就好比,我(A服务器)去超市(B服务器)买东西,当我出超市时,如果没有CORS方法,保安(浏览器)会拦截住我买的东西。
而如果保安看到了我的购物小票(一些标识),就会把这些东西给我,让我带走。
由于JSONP跨域仅支持客户端发get请求(跨域请求通常也只发get请求就够用了),但如果要发get/post/put/delete这几种ajax请求,就要用CORS。
由于当下的跨域的业务比较常见,所有的主流的浏览器默认支持跨域.
CORS的核心是 需要在被访问的服务器端配置 是否允许被跨域访问。
1.4.1 前情回顾
在没用JSONP和CORS时,A服务器尝试跨域访问B服务器时,浏览器中的报错信息是:
翻译一下就是:
1.4.2 CORS实现跨域访问的原理
通过在B浏览器中添加配置类,进而返回的Response对象上就被标识了各种标签。
浏览器看到后,就不拦截了。
1.4.3 编辑CORS配置类
在jt-common中添加CORS的配置类
@Configuration
public class CORSConfig implements WebMvcConfigurer {
//记得这个CORSConfig类要实现WebMvcConfigurer接口
//而这个WebMvcConfigurer接口就相当于tomcat服务器安装目录下的那个web.xml配置文件
/**
* 通过CORS实现跨域请求
* 需要配置服务器端程序,通过配置了这些内容,被请求的服务器就允许这样的跨域请求访问它,所以浏览器知道了那个被请求的服务器允许这些了。数据回到浏览器时,浏览器就不拦截了。
* 方法说明:
* 1.addMapping("/**") 允许访问哪些资源时可以跨域(比如/item,/itemDesc,/item/42358.html,/itemDesc/test.html)
* "/**" 就是可以访问任意目录下的任意资源时,可以跨域请求访问
* 2.allowedOrigins("*") 允许哪个服务器可以去发起跨域访问请求,括号里填想发起跨域访问的服务器的url 比如:http://www.jt.com:80/)
* "*" 就是任意url的服务器都可以去发起跨域请求,并得到结果
* 3.allowCredentials(true) 服务器在发送跨域请求时,是否可以携带cookie等参数 true表示可以携带
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true);
}
}
1.4.4 CORS实现跨域访问的效果测试
前提:
jt-manage服务器返回的json字符串改为普通的形式:
jt-web假装访问的是自己家的test.html文件,在浏览器中的url为www.jt.com/test.html
实际上,通过test.html文件会去访问jt-manage中的test.json