前端同源策略和跨域详解

同源策略和跨域

同源策略

同源:两个页面的协议、域名、端口号都相同

同源策略:是浏览器提供的一个安全功能(浏览器规定,非同源网站JS不能进行资源交互)

  • 无法接触Cookie、LocalStorage、IndexedDB;防止恶意网站通过js获取用户其他网站的cookie
  • 无法接触DOM,因为恶意网站可以通过iframe打卡银行界面,如果可以获取DOM就相当于可以获取整个银行界面的信息
  • 无法发送ajax请求

跨域

不符合同源策略的行为
请添加图片描述

浏览器允许发起跨域请求,但是跨域请求回来的数据会被浏览器拦截

如何实现跨域

  • JSONP:出现的早,兼容性好。只支持get请求。
  • CORS:出现的晚,支持head、get、post。不兼容低版本的浏览器。
  • 使用 proxy反向代理
JSONP

由于浏览器同源策略的限制,网页无法通过Ajax请求非同源的接口数据,但是<script>等标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。

不受同源策略影响的标签scriptimglinkiframe

  • 体验script标签
<!-- script引入同源 -->
<script src="./1.jsonp.js"></script>
<!-- script引入非同源 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>

请添加图片描述

可以看出无论是同源下的1.jsonp.js文件还是非同源的内容都能正确请求到服务器内容

  • 实现原理:通过script标签的src属性,请求跨域的数据接口,并通过函数调用接受跨域接口响应回来的数据
<script>
  function fn(result) {
      
      
    console.log(result);
  }
</script>
<script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=走出自闭的鸟儿&cb=fn"></script>

请添加图片描述

上面的例子可以看出,我们没有主动执行fn()函数,就实现了获取百度的数据并打印。

请添加图片描述

从上图可以看出JSONP需要服务器端支持,因为服务端要接收callback且返回对应格式执行

JSONP不属于ajax,因为没有使用xhr

因为在类似url资源请求中,所以JSONP只支持get请求,且不安全

  • JSONP实操

我们创建一个server.js表示当前目录的http服务,端口号为90

var express = require('express')

// 90端口的服务,将当前目录作为http服务
var app = express()
app.use(express.static(__dirname))
app.listen(90)

再创建一个index.html显示hello

此时90端口的服务会自动调用当前目录的index.html,显示hello
请添加图片描述

接下来我们在server.js添加一个91端口的服务,它支持get请求返回’你好’

// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
    
    
  res.send('你好')
})
app2.listen(91)

我们在90端口向91端口发起fetch请求

<script>
  fetch("http://localhost:91").then(res => res.text()).then(data => {
      
       alert(data) })
</script>

此时产生了跨域,报错

请添加图片描述

我们采用JSONP解决

服务端

// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
    
    
  var funcname = req.query.callback
  res.send(funcname + "('你好')")
  // 相当于返回fn('你好')
})
app2.listen(91)

客户端

<script>
  function fn(data) {
      
      
    alert(data)
  }
</script>
<script src="http://localhost:91?callback=fn"></script>

请添加图片描述

CORS跨域资源共享

服务端来配置响应头

header(“Access-Control-Allow-Origin:*”)

// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
    
    
  res.header('Access-Control-Allow-Origin', '*')
  res.send('你好')
})
app2.listen(91)

客户端直接发请求即可

<script>
  fetch("http://localhost:91").then(res => res.text()).then(data => {
      
       alert(data) })
</script>

请添加图片描述

上面的配置是最基础的,实际项目中我们还有更加细化的配置

// ALLOW_ORIGIN要么是一个*,表示接受任意域名的请求;要么就设置具体域名
res.header('Access-Control-Allow-Origin', ALLOW_ORIGIN)
//  ALLOW_ORIGIN是一个布尔值,表示是否允许发送cookie;设置为允许时,ALLOW_ORIGIN要设为具体域名
res.header('Access-Control-Allow-Credentials', ALLOW_ORIGIN)
// 支持返回除6个基本字段外的其他字段
res.header('Access-Control-Expose-Headers', ALLOW_ORIGIN)
// ...

有时还会配置一个options用来先判断一次是否允许跨域,这就是发起请求会返回一个options和一份数据的原因;options请求是跨域请求之前的预检查,会返回服务端支持的请求方法(get、post…)

http proxy =>webpack

代理服务器

因为浏览器同源策略只针对于ajax,并不限制服务器之间的通信传输,我们在客户端和服务器中间使用一个代理服务器,代理服务器和客户端同源,代理服务器和服务器进行数据交互,这样就实现了跨域。

在webpack.config.js中配置

module.exports = defineConfig({
    
    
  transpileDependencies: true,
  devServer:{
    
    
    proxy:{
    
    
        // 请求为/api,就会触发代理
      '/api':{
    
    
        target:'http://localhost:91', // 服务端接口地址
      }
    }
  }
})

客户端

fetch("/api").then(res => res.text()).then(data => {
    
     alert(data) })

其他代理总体思路相同,涉及服务器部署问题

其他不常用的跨域方式
  • postMessage

具体使用方法可以参考MDN

原理是一个窗口(客户端)可以获得另一个窗口(服务端)的引用,对获取的引用使用postMessage()方法分发消息,而接收消息的窗口可以获取消息并经过处理后返回

  • websocket协议跨域

webcoket作为一种常用于实时聊天的协议,本身就不存在跨域问题,利用websocket的api创建一个socket实例,利用open方法向后台发送数据,利用message方法接收后台的数据。

猜你喜欢

转载自blog.csdn.net/qq_47234456/article/details/127020230
今日推荐