一, 跨域原理 (同源策略)
在项目搭建的初期, 因为现在项目基本上都是前后端分离, 所以不可避免地会遇到跨域问题, 而造成跨域的罪魁祸首就是浏览器的同源策略. 所以要解决跨域,
我们必须知道什么是浏览器的同源策略.
1, 什么是同源策略
概念 它是一个著名的安全策略. 所有支持 JavaScript 的浏览器都会使用这个策略. 所谓同源是指, 域名, 协议, 端口相同.
当协议, 子域名, 主域名, 端口号中任意一个不相同时, 都算作不同域. 不同域之间相互请求资源, 就算作 "跨域".
注意 跨域并不是请求发不出去, 请求能发出去, 服务端能收到请求并正常返回结果, 只是结果被浏览器拦截了.
2, 同源策略限制什么
概念 同源策略限制 从一个源加载的文档或脚本如何与来自另一个源的资源进行交互. 这是一个用于隔离潜在恶意文件的关键的安全机制. 它的存在可以保护
用户隐私信息, 防止身份伪造等 (读取 Cookie).
同源策略限制内容有
,Cookie,LocalStorage,IndexedDB 等存储性内容
,DOM 节点
,Ajax 请求不能发送
但是有三个标签是允许跨域加载资源
- <script src=xxx>
- < img src = xxx > <link href = xxx >
也就是说这三个标签可以不受同源策略限制, 这也是为什么使用 < img > 标签的时候, 可以引用外部的服务器图片.
解决跨域的方式很多, 这里介绍两种最简单的方式: JSONP 和 CORS.
二, JSONP
原理: <script > 是可以跨域的, 通过动态创建 script 标签, 然后利用 src 属性进行跨域.
1,JavaScript 示例
前端代码
- <html lang="en">
- <head>
- <title>jsoup</title>
- <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
- <script>
- // 1. 定义方法 这个是会被回调的函数
- function coming(data) {
- console.log(data);
- }
- </script>
- <script>
- // 2. 动态拼接 < script > 标签, 把需要调用的本地方法名传递给后端
- let url = "http://localhost:8088/getUser/1?callback=coming";
- // 平时都是手写 < script src="">, 现在我们在代码中动态构造 script 标签并设置 src 属性
- let script = document.createElement('script');
- script.type = "text/javascript";
- script.src = url;
- // 把 script 标签加入 head(因为 script 标签本来也是在 HTML 的 head 中), 并发起请求, 后端响应后浏览器会自动执行返回的 script 片段
- document.getElementsByTagName('head')[0].appendChild(script);
- </script>
- </head>
- </HTML>
后段代码
- @RestController
- public class UserController {
- @GetMapping(value = "/getUser/{id}")
- public String getUser(@PathVariable("id") String id, String callback) {
- // 拼接方法名 + 参数 (这就会调用上面 script 中的 coming 方法)
- return callback + "({\"username\": \" 小小 \",\"age\": 4"})";
- }
- }
2,jQuery 示例
上面说过了, JSONP 其实算是一种对同源策略规则的投机取巧, 实现方式可以多种多样. jQuery 对 JSONP 也有实现
- <HTML lang="en">
- <head>
- <title>jQuery 示例 </title>
- <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
- </head>
- <body>
- <input type="text" id="result">
- <input type="button" onclick="onClick()" value="点击">
- </body>
- <script>
- /**
- * 照常使用 Ajax, 只要把 dataType 改为 "jsonp" 即可.
- * jQuery 在前端替我们做了两件事:
- * 1.URL 后拼接方法名 (随机生成)
- * 2. 解析从后端得到的 JS 片段, 把正确的值传入 success:function()
- *
- * 后端还是要按原来的做 jQuery 自动帮我们在 URL 里加了 callback 参数.
- */
- function onClick() {
- $.Ajax({
- type: "GET",
- url: "http://localhost:8088/getUser/1",
- dataType: "jsonp",
- success: function(data){
- $('#result').val(data.name);
- }
- });
- }
- </script>
- </HTML>
总结
,JSONP 都是 GET 和异步请求的, 不存在其他的请求方式和同步请求, 且 jQuery 默认就会给 JSONP 的请求清除缓存.
,JSONP 优点是 兼容性好, 可用于解决主流浏览器的跨域数据访问的问题. 缺点是 仅支持 get 方法具有局限性.
三, CORS
1,CORS 原理
浏览器认为只要后端没返回 CORS 头 (Access-Control-Allow-Origin), 就认为后端不允许跨域, 返回的数据不可靠. 所以只要后端能够返回浏览器需要的请求头,
即可跨域 (响应数据就不会被同源策略抛弃), 这个是表层原理.
不过目前不是所有浏览器都支持该功能 (会自动带上请求头),IE 浏览器不能低于 IE10. 所以最终来看, CORS 这种方案不需要前端做任何事情, 只需后端配合即可.
2, 示例
前端代码
- <HTML lang="en">
- <head>
- <script type="text/javascript" src="/jquery/jquery-2.1.3.min.js"></script>
- </head>
- <body>
- <input type="text" id="result">
- <input type="button" onclick="onButtonClick()" value="get_button">
- </body>
- <script>
- /**
- * 普通 Ajax, 没有设置 JSONP
- */
- function onButtonClick() {
- $.Ajax({
- type: "GET",
- url: "http://localhost:8080/getUser/1",
- success: function(data){
- $('#result').val(data.name);
- }
- });
- }
- </script>
- </HTML>
后段代码
方式一 方法上加 @CrossOrigin
- @RestController
- public class UserController {
- /**
- * 在跨域方法上加 @CrossOrigin 即可完美解决跨域问题
- */
- @CrossOrigin("http://localhost:8088")
- @GetMapping(value = "/getUser/{id}")
- public User getUser(@PathVariable("id") String id) {
- User user = new User();
- user.setName("小小");
- user.setAge(4);
- return user;
- }
- }
方式二 Controller 上加 @CrossOrigin
@CrossOrigin 还可以加载 Controller 上, 这样 Controller 的所有方法都支持跨域.
- @RestController
- @CrossOrigin("http://localhost:8088")
- public class UserController {
- @GetMapping(value = "/getUser/{id}")
- public User getUser(@PathVariable("id") String id) {
- User user = new User();
- user.setName("小小");
- user.setAge(4);
- return user;
- }
- }
方式三 @Bean 配置跨域 Filter
- @Configuration
- public class CorsConfig {
- @Bean
- public CorsFilter corsFilter() {
- //1. 添加 CORS 配置信息
- CorsConfiguration config = new CorsConfiguration();
- //1) 允许的域, 如果写 *, 那么 cookie 就无法使用了
- config.addAllowedOrigin("http://localhost:8088");
- //2) 是否发送 Cookie 信息
- config.setAllowCredentials(true);
- //3) 允许的请求方式
- config.addAllowedMethod("*");
- // 4) 允许的头信息
- config.addAllowedHeader("*");
- // 5) 有效时长
- config.setMaxAge(3600L);
- //2. 添加映射路径, 我们拦截一切请求
- UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
- configSource.registerCorsConfiguration("/**", config);
- //3. 返回新的 CorsFilter.
- return new CorsFilter(configSource);
- }
- }
总结 本人在实际开发中都是通过 CORS 解决跨域问题, 而且是通过第三种方式配置全局的 bean 对象.
参考
1, 同源策略与跨域 https://zhuanlan.zhihu.com/p/104984869
2,JavaScript 跨域的四种方式介绍 https://www.php.cn/js-tutorial-412039.html
3, 什么是 JS 跨域访问?
``` 别人骂我胖, 我会生气, 因为我心里承认了我胖. 别人说我矮, 我就会觉得好笑, 因为我心里知道我不可能矮. 这就是我们为什么会对别人的攻击生气. 攻我盾者, 乃我内心之矛 (7). ```
来源: https://www.cnblogs.com/qdhxhz/p/12347155.html