浏览器跨域及解决跨域方案

147 阅读3分钟

1. 同源策略

  • 什么是同源策略?为什么会有同源策略?在什么环境里会有同源策略?

1.1. 同源策略的概念

  • MDN 上给同源策略的定义:

    • 同源策略是一个重要的安全策略,它用于限制一个的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
  • 比较抽象,总结一下:

    • 如果两个URL 的 协议、主机、端口 都相同的话,那么就可以说这两个URL 是同源
    • 也被称为 “协议/主机/端口元组”,或者直接称为“元组”

1.2. 产生跨域的原因

  • 当浏览器发现,静态资源和API 接口请求不是来自同一个源,就会产生跨域
  • 而早期的项目是没有跨域问题的,这是为什么?
    • 这个和早期的服务器渲染有关系:早期网址请求服务器的时候,服务器会返回一个完整的HTML 页面,而页面内的外链都是去同一个源去下载的,所以没有跨域问题。
    • 随后到了前后端分离阶段,目前前端开发的代码和服务器开发的API接口往往都是分离的,甚至是部署在不同服务器上面的,而服务通常不是部署在同一个源中的,所以静态资源区请求API资源就会产生跨域问题。

2. 跨域解决方案

  • 这里我只介绍目前常用的跨域解决方案,由于我是个前端programmer,所以我会用js来写-_-

2.1 动静合并

  • 将打包后的静态资源和API服务器放在一起, 话不多说,上代码:(这里使用nodejs、koa 为例)
const Koa = require('koa')
const KoaRouter = require('@koa/router')
const static = require('koa-static')

const app = new Koa()

// ../dist 文件就是打包的静态资源文件夹
app.use(static('../dist'))

const router = new KoaRouter({ prefix: '/test'})
router.get('/demo', (ctx, next) => {
  ctx.body = [
    { name: 'IPhone', price: '100' },
    { name: 'IPad', price: '50' },
    { name: 'MacBook pro', price: '101' }
  ]
})

app.listen(8800, () => {
  console.log('http://localhost:8800 服务启动成功~')
})

2.2 CORS 方案

  • 跨源资源共享(CORS,Cross-Origin Resource Sharing 跨域资源共享)
    • 它是一种 http header 的机制
    • 该机制通过允许服务器表示除了他自己以外的其他院(域、协议、端口),使得浏览器允许这些origin 访问加载自己的资源
    • 浏览器将CORS 请求分为两类:简单请求、非简单请求
    • 这里不多介绍这两种请求
    • 话不多说,上服务端代码:
app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', '*')
  
  // 下面有没有都行,现在的浏览器不限制了
  ctx.set('Access-Control-Allow-Headers', 'Accept,Accept-Encoding,Accept-Language,Connection,Content-Length,Content-Type,Host,Origin,Referer,User-Agent')
  ctx.set('Access-Control-Allow-Credentials', true)
  ctx.set('Access-Control-Allow-Methods', 'PUT,POST,GET,PATCH,DELETE,OPTIONS')
  
  if (ctx.method === 'OPTIONS') {
    ctx.status = 204
    ctx.body = ''
  } else {
    await next()
  }
  next()
})

2.3 node代理方案

  • node 代理通常借助一个插件 http-proxy-middleware
  • 和webpack 的配置差不多, 而且webpack、vite里面也是使用的这个插件
  • 上代码:
const { createProxyMiddleware} = require('http-proxy-middleware')
// 使用的是koa
app.use('/api', createProxyMiddleware({
  target: 'http://localhost:8080', // API服务器端地址
  pathRewrite: {
    '^/api': ''
  },
  changeOrigin: true // API服务器可能会校验源,所以配这个
}))

2.4 nginx 反向代理

location / {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers 'Accept,Accept-Encoding,Accept-Language,Connection,Content-Length,Content-Type,Host,Origin,Referer,User-Agent'
    add_header Access-Control-Allow-Credentials true
    add_header Access-Control-Allow-Methods 'PUT,POST,GET,PATCH,DELETE,OPTIONS'
    if ($request_method = 'OPTIONS') {
        return 204
    }
    
    proxy_pass http://localhost:8000
}

结束~