IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Web前端——跨域问题 -> 正文阅读

[JavaScript知识库]Web前端——跨域问题

1.跨域问题

什么是跨域?
首先,现代浏览器为了安全,做了一个同源限制.
也就是所谓的同源安全策略.
本质上,其实是不存在所谓的跨不跨域的.
把浏览器想象成一个发送网络请求的软件.
按照道理来说,请求都是可以发送出去的.
但是在 web 端,浏览器里,这么做的就不合适.
如果网络上的接口可以不受限制的被任意人调用.
那将是一个非常混乱的场景。

所以,为了防止这种情况,浏览器做了这个同源策略来防止这种情况发生.
对,一般服务器不管这个事情.
跨域指的是,A网站内部向B网站发送一个AJAX请求.
由于浏览器有同源策略的限制,默认情况下,是不允许 A网站向 B 网站请求数据资源的.

http://my.website.com/ ---> http://your.website.com/ 是不允许的.
具体来说,凡是发送请求中,协议,主机名,端口号 有一个不同,即为跨域请求.
上述例子:my.website.com 和 your.website.com 就属于主机名不同而导致的跨域.

在这里插入图片描述

总结一句:
协议相同 + 域名相同 + 端口号相同
浏览器才认为是同一个网站.
才不同受到同源策略的影响.
才可以正常的发送 AJAX 请求.
其他情况下,发送 AJAX 请求,都属于跨域.

请注意: 这里说的是 XMLHttpRequest 下的 AJAX 请求.
对于 <img> , <script>, <link> 等标签,就不存在跨域请求.(除非对方后台做了防盗链)

所谓的同源策略是浏览器实现的,而和后台服务器无关.
A 发送请求到 B. 请求实际上是发送到了 B 后台, B后台接受到数据后.
其实也有返回.
只不过,这个数据返回到浏览器之后,浏览器把这个数据给劫持了.不让A网站使用。

2.为什么要跨域?

因为当一个项目变大时,把所有的内容都丢在一个网站或者是后台服务器中是不现实的.
比如:
一个网站体量很大.有很多可以独立且复杂的业务
? 比如有一个订单管理的后台数据API网站服务.
? 比如有一个用户管理的后台数据API网站服务.
? 比如有一个新闻管理的后台数据API网站服务.
最后剩下的就是web的UI层面的东西.
把这个UI层面的东西和哪个数据服务API的网站集成在一起比较合适呢?
都不适合.它应该是一个专门的网站.

在这里插入图片描述

所以域名可能存在如下情况:
?	主网站(UIWeb)域名为: http://www.relax.com/ 
?	订单数据服务接口域名为: https://order.relax.com/orderList 
?	用户数据服务接口域名为: https://users.relax.com/userList 
?	新闻数据服务就扣域名为: http://news.relax.com/newList 
所以,UIWeb 如果需要请求这数据.
?	请求订单数据: 协议不同 https|http , 二级域名不同 order | www , 端口号不同 443|80 --> 跨域
?	请求用户数据: 协议不同 https|http , 二级域名不同 users | www , 端口号不同 443|80 --> 跨域
?	请求新闻数据列表: 协议相同 http|http , 二级域名不同 order | www , 端口号相同 80|80 --> 跨域
不管请求哪个后台服务器的数据,都存在跨域的情况.
也就是无法利用 ajax 异步的获取到数据内容.

演示跨域问题

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<button type="button" onclick="sendAjax()">发送Get请求</button>
	</body>
	<script type="text/javascript">
		function sendAjax() {
			//创建对象
			var xmlhttp = new XMLHttpRequest();
			//发送请求
			xmlhttp.open("GET", "https://tool.bitefu.net/shouji/?mobile=13259141515", true);
			xmlhttp.send();
			//接收服务器的响应
			xmlhttp.onreadystatechange = function() {
				if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
					//接收后台响应的json字符串
					 var jsonStr=xmlhttp.responseText;
					 console.log(jsonStr);
					//var jsonObj= JSON.parse(jsonStr);
				}
			}
		}
	</script>
</html>

3.利用jsonp进行跨域

jsonp 跨域是利用在浏览器中,script标签是不受同源策略限制的特性.
JSOP 处理跨域的原理是,利用某些 html 标签不受同源策略的限制,来发送请求。比如 img script
所以,我就利用 script 来进行跨域请求

具体实现原理大概分下面几步
?	客户端动态的创建一个 script 标签
?	设置 script 标签的src 为跨域的服务器后台.
?	src 中需要带一个 callback 查询字符串,告诉后台,前端提供的方法名是什么.
?	然后后端,直接返回一一串 callback(data) 的字符串给浏览器即可.

前端端口是:8080

button.addEventListener('click', function () {
    // 利用下载script标签是不存在浏览器同源策略的特点.
    const scriptTag = document.createElement('script')
    scriptTag.type = 'text/javascript'
    // 第一 22222 端口号不同.
    // 第二 callback=show 是传递给22222后台服务器的查询字符串参数.
    // 第三 后台服务器根据拿到了这个callback = show,往前端输出一些文本流.(这里就是和利用下载script标签一模一样.)
    scriptTag.src = 'http://localhost:12345/getUserinfo?callback=show'
    document.body.appendChild(scriptTag)
  },false)
  // 这里的show 方式就是上述 jsonp 中的 callback 查询字符串指定的值.
  function show (user) {
    let dataStr = ""
    for(let prop in user) {
      dataStr += prop  + ":" + user[prop] + "<br>"
    }
    unOriginPlaceHolder.innerHTML = dataStr
  }

后端端口是:12345

const app = require('express')()
const url = require('url')
const queryString = require('querystring')
app.get('/getUserInfo', (request, response) => { 
  const queryObj = url.parse(request.url, true).query
  const callback = queryObj.callback // 获取callback参数

  const data = {
    userid: 1,
    name: '李四',
    age: 22,
    profession: '软件工程师'
  }
  const objStingIfy = JSON.stringify(data)
  console.log(objStingIfy)
  // 拼接 callback(数据) 字符串,返回给前端
  response.end(`${callback}(${objStingIfy})`)
})
app.listen(22222, () => { 
  console.log('服务启动成功!')
})

结果:
在这里插入图片描述

jsonp使用总结:
? 利用 script 标签异步加载,而非传统的ajax
? 异步 script 加载不受浏览器同源策略限制.
? 给 script.src=callback=fn来告知请求的后台前端提供的fn是什么
? 基本就是前端提供方法,后端提供数据并调用前端的这个方法.
? jsonp 只支持 get 请求.(本质上,不就是下载文件吗?下载文件一般都是get请求)

演示jsonp跨域

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<button type="button" onclick="send()">发送请求</button>
	</body>
	<script type="text/javascript">
		//获取后台响应数据的回调函数名
		function getJsonData(res) {
			console.log(res);
			alert(res.isp);
		}
		function send(){
			var sc=document.createElement("script");
			sc.src="https://tool.bitefu.net/shouji/?mobile=13259141515&callback=getJsonData"
		}
	</script>
	<!-- 利用script标签不受同源策略的影响来发送跨域请求。
	 callback=getJsonData 指定jsonp方式回调函数。这个参数是大家利用jsonp的方式约定俗成的一个参数。
	 一般这个参数的名字叫做 callback ,除非他指定了
	 JSONP 方式后台返回的数据
	 getJsonData({"name":"zhangsan","age":23})
	getJsonData({"isp":"\u4e2d\u56fd\u8054\u901a GSM","province":"\u9655\u897f","city":"\u5546\u6d1b","postcode":"726000","citycode":"0914","areacode":"611000","status":1,"mobile":"13259141515"})
	 -->
	<script src="https://tool.bitefu.net/shouji/?mobile=13259141515&callback=getJsonData"></script>
</html>

演示用jQuery的方式发送jsonp请求

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<button type="button">jsonp请求</button>
	</body>
	<script type="text/javascript">
		function hehe(data) {
			alert(data.isp);
		}
		$('button').click(function() {
			$.ajax({
				type: "GET",
				url: "https://tool.bitefu.net/shouji/",
				data: {
					mobile: "13898138919"
				},
				//指定 callback
				jsonp: "callback", //jsonp 请求 回调函数名Jqurey会自动生成 
				//jsonpCallback: 'hehe', // 你要指定回调函数名,jsonpCallback: 'hehe', 可以自己定义,jsonp的回调函数名
				success: function(res) {
					console.log(res);
				},
				//后台返回的数据类型
				dataType: "jsonp"
			});
		})
		//https://tool.bitefu.net/shouji/?callback=jQuery111307619785128408234_1642557156066&mobile=13259141515&_=1642557156067
		// jQuery111303327070950733343_1642557233081({"isp":"\u4e2d\u56fd\u8054\u901a GSM","province":"\u9655\u897f","city":"\u5546\u6d1b","postcode":"726000","citycode":"0914","areacode":"611000","status":1,"mobile":"13259141515"})
		//JSONP 不足之处,就是只支持get请求。
		//天气预报,笑话接口 ,用的是cors这种方式来解决跨域问题的。
	</script>
</html>

简化方法发送jsonp

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<button type="button">jsonp请求</button>
	</body>
	<script type="text/javascript">
		$('button').click(function() {
			$.getJSON("https://tool.bitefu.net/shouji/?mobile=13259141515&callback=?", function(res) {
				alert(res.isp);
			});
		})
	</script>
</html>

4.用cros进行跨域

cors 是 Cross-Origin-Resource-Sharing 的缩写.
也是现代浏览器普遍支持的一种非常方便的跨域方案.
它的具体工作流程是:
浏览器在检测到你发送的 ajax 请求不是同源请求时,会自动在 http 的头部添加一个 origin 字段.

在这里插入图片描述
请求怎么都能发送出去,我们拿不到数据是因为浏览器在中间做了一层劫持.
在这里插入图片描述
在这里插入图片描述

获取不到数据的原因也很简单:
? 这是一次跨域请求.
? 请求确实发送到服务器了.
? 服务器也把数据返回到了浏览器.
? 但是服务器返回的响应头里,没有告诉浏览器哪个域名可以访问这些数据(也就是没有设置 Access-Control-Allow-Origin)
? 于是浏览器就把这个数据丢弃了.我们也就无法获取到这个数据.
这个时候,只需要后台在相应头里加上一个 Access-Control-Allow-Origin:* 即可完成跨域数据获取.

在这里插入图片描述

CORS 跨域方案简单总结:
? 当浏览器发送一个请求是跨域的时候,会自动加上 Origin
? 需要后台配合设置响应头 Access-Control-Allow-Origin:*|Origin即可完成跨域.
? CORS 支持 GET,POST 常规请求.
? 同时也支持 PUT,DELETE等非 post,get的请求,但会先发一次预检请求.

在这里插入图片描述

cros跨域演示

后台代码

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value = "/login", name = "LoginServlet")
public class LoginServlet extends HttpServlet {
    //处理post请求
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //* 后台信任来自任意服务器的请求,告诉浏览器你不要拦截
        //http://127.0.0.1:8848
        response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848");
        //获取前台提交的用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //查询数据库去比对
        //{"status":200,"msg":"ok","username":"张三"}
        //{"status":500,"msg":"no"}
        response.setContentType("application/json;charset=utf-8");
        if (username.equals("zhangsan") && password.equals("123456")) {
            response.getWriter().write("{\"status\":200,\"msg\":\"ok\",\"username\":\"张三\"}");
        } else {
            response.getWriter().write("{\"status\":500,\"msg\":\"no\"}");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

前台代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <div id="msg">

        </div>
        <button type="button" onclick="sendPost()">Ajax之post请求</button>
    </body>
</html>
<script type="text/javascript">
    function sendPost() {
        //1.创建ajax对象
        var ajax = new XMLHttpRequest();
        //2.连接服务器
        ajax.open('post', 'http://localhost:8080/login', true);
        //3.设置请求头信息
        ajax.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
        //4.发送请求post方式,把请求参数放到send()方法里面
        var username = "zhangsan555";
        var password = "123456"
        ajax.send("username=" + username + "&password=" + password);

        //接收响应
        ajax.onreadystatechange = function () {
            if (ajax.readyState == 4 && ajax.status == 200) {
                var c = ajax.responseText;
                //alert(c);
                var obj = JSON.parse(c);
                if (obj.status == 200) {
                    //window.open("index.html");
                    location.href = "index.html";
                } else {
                    document.getElementById('msg').innerHTML = "<h1 style='color:red'>用户名或密码错误<h1/>"
                }
            }
        }
    }
</script>

5.proxy模式简介

核心思想是:让前端请求我们自己的后台,让后台去跨域请求真是的数据,然后把数据返回给前台.

实现方式,可以利用 nginx 做反向代理.以及我们自己写一个后台中转的服务器.

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-28 11:49:07  更:2022-01-28 11:49:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:28:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码