0


Web前端——跨域问题

目录

1.跨域问题

  1. 什么是跨域?
  2. 首先,现代浏览器为了安全,做了一个同源限制.
  3. 也就是所谓的同源安全策略.
  4. 本质上,其实是不存在所谓的跨不跨域的.
  5. 把浏览器想象成一个发送网络请求的软件.
  6. 按照道理来说,请求都是可以发送出去的.
  7. 但是在 web 端,浏览器里,这么做的就不合适.
  8. 如果网络上的接口可以不受限制的被任意人调用.
  9. 那将是一个非常混乱的场景。
  10. 所以,为了防止这种情况,浏览器做了这个同源策略来防止这种情况发生.
  11. 对,一般服务器不管这个事情.
  12. 跨域指的是,A网站内部向B网站发送一个AJAX请求.
  13. 由于浏览器有同源策略的限制,默认情况下,是不允许 A网站向 B 网站请求数据资源的.
  14. http://my.website.com/ ---> http://your.website.com/ 是不允许的.
  15. 具体来说,凡是发送请求中,协议,主机名,端口号 有一个不同,即为跨域请求.
  16. 上述例子:my.website.com your.website.com 就属于主机名不同而导致的跨域.

在这里插入图片描述

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

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

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

2.为什么要跨域?

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

在这里插入图片描述

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

演示跨域问题

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

3.利用jsonp进行跨域

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

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

前端端口是:8080

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

后端端口是:12345

  1. const app = require('express')()
  2. const url = require('url')
  3. const queryString = require('querystring')
  4. app.get('/getUserInfo', (request, response) => {
  5. const queryObj = url.parse(request.url, true).query
  6. const callback = queryObj.callback // 获取callback参数
  7. const data = {
  8. userid: 1,
  9. name: '李四',
  10. age: 22,
  11. profession: '软件工程师'
  12. }
  13. const objStingIfy = JSON.stringify(data)
  14. console.log(objStingIfy)
  15. // 拼接 callback(数据) 字符串,返回给前端
  16. response.end(`${callback}(${objStingIfy})`)
  17. })
  18. app.listen(22222, () => {
  19. console.log('服务启动成功!')
  20. })

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

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

演示jsonp跨域

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

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

  1. <!DOCTYPEhtml><html><head><metacharset="utf-8"><title></title><scriptsrc="js/jquery.js"type="text/javascript"charset="utf-8"></script></head><body><buttontype="button">jsonp请求</button></body><scripttype="text/javascript">functionhehe(data){alert(data.isp);}$('button').click(function(){
  2. $.ajax({
  3. type:"GET",
  4. url:"https://tool.bitefu.net/shouji/",
  5. data:{
  6. mobile:"13898138919"},//指定 callback
  7. jsonp:"callback",//jsonp 请求 回调函数名Jqurey会自动生成 //jsonpCallback: 'hehe', // 你要指定回调函数名,jsonpCallback: 'hehe', 可以自己定义,jsonp的回调函数名success:function(res){
  8. console.log(res);},//后台返回的数据类型
  9. 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

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

4.用cros进行跨域

  1. cors Cross-Origin-Resource-Sharing 的缩写.
  2. 也是现代浏览器普遍支持的一种非常方便的跨域方案.
  3. 它的具体工作流程是:
  4. 浏览器在检测到你发送的 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跨域演示

后台代码

  1. importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@WebServlet(value ="/login", name ="LoginServlet")publicclassLoginServletextendsHttpServlet{//处理post请求protectedvoiddoPost(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{//* 后台信任来自任意服务器的请求,告诉浏览器你不要拦截//http://127.0.0.1:8848
  2. 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"}
  3. response.setContentType("application/json;charset=utf-8");if(username.equals("zhangsan")&& password.equals("123456")){
  4. response.getWriter().write("{\"status\":200,\"msg\":\"ok\",\"username\":\"张三\"}");}else{
  5. response.getWriter().write("{\"status\":500,\"msg\":\"no\"}");}}protectedvoiddoGet(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{this.doPost(request, response);}}

前台代码

  1. <!DOCTYPEhtml><html><head><metacharset="utf-8"><title></title></head><body><divid="msg"></div><buttontype="button"onclick="sendPost()">Ajax之post请求</button></body></html><scripttype="text/javascript">functionsendPost(){//1.创建ajax对象var ajax =newXMLHttpRequest();//2.连接服务器
  2. ajax.open('post','http://localhost:8080/login',true);//3.设置请求头信息
  3. ajax.setRequestHeader('content-type','application/x-www-form-urlencoded');//4.发送请求post方式,把请求参数放到send()方法里面var username ="zhangsan555";var password ="123456"
  4. ajax.send("username="+ username +"&password="+ password);//接收响应
  5. 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");
  6. location.href ="index.html";}else{
  7. document.getElementById('msg').innerHTML ="<h1 style='color:red'>用户名或密码错误<h1/>"}}}}</script>

5.proxy模式简介

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

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

标签: 前端 服务器 ajax

本文转载自: https://blog.csdn.net/weixin_43496491/article/details/122643668
版权归原作者 Geek Li 所有, 如有侵权,请联系我们删除。

“Web前端&mdash;&mdash;跨域问题”的评论:

还没有评论