瀏覽器跨域

冰凉小手發表於2024-05-24

同源策略

同源策略是瀏覽器的行為,是為了保護本地資料不被JavaScript程式碼獲取回來的資料汙染,因此攔截的是客戶端發出的請求返回的資料接收,即請求傳送了,伺服器響應了,但是瀏覽器不接收。

同源:請求要滿足協議,域名,埠都相同
http://www.test:8080/apihttp://www.test:8080/test 同源

JSONP

同源策略針對的AJAX請求,對script,img標籤沒有該限制。JSONP基於此,讓伺服器返回一段js程式碼。該段程式碼是一個約定好的函式的執行,資料透過引數傳遞。

瀏覽器程式碼:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

</body>
<script>
  function callback(data) {
    console.log(data)
  }
  function jsonp(url) {
    const script = document.createElement('script')
    script.src = url
    document.body.appendChild(script)
    script.addEventListener('load', () => {
      script.remove()
    })
  }

  const url = 'http://localhost:5008/getData'
  jsonp(url)

</script>

</html>

伺服器程式碼

const express = require('express')

const app = express()
const port = 5008

const router = express.Router()
router.get('/getData', (req, res) => {
  const data = {
    name: '小明',
    age: '18'
  }
  const script = `callback(${JSON.stringify(data)})`
  res.header('content-type', 'application/javascript').send(script)
})

app.use(router)

app.listen(port, () => {
  console.log(`server listen to ${port}`)
})

缺陷

  • 會影響伺服器的正常響應格式:JSONP要求伺服器返回一段js程式碼,在非跨域時又是正常的JSON格式
  • 只能使用GET請求:script是GET請求

CORS

瀏覽器程式碼:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const getCookie = 'http://localhost:5010/getCookie'
    const url = 'http://localhost:5010/getData'
    fetch(getCookie).then(() => {
      fetch(url, {
        method: 'get',
        headers: {
          a: 1
        },
        credentials: "include"
      })
    })
  </script>
</body>

</html>

伺服器程式碼

const express = require('express')
const cookieParser = require('cookie-parser')
const CORSModule = require('./cors')
const router = require('./router')
// const cors = require('cors')

const app = express()
const post = 5010
app.use(cookieParser)
// const allowOrigins = ['http://127.0.0.1:5500']
// app.use(cors({
//   origin: (origin, callback) => {
//     if (!allowOrigins.includes(origin)) {
//       callback(new Error('not allowed'))
//     }
//     callback(null, true)
//   },
//   methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
//   credentials: true
// }))
app.use(CORSModule)
app.use(router)

app.listen(post, () => {
  console.log(`server listen to ${post}`)
})
const allowOrigins = ['http://127.0.0.1:5500']

module.exports = (req, res, next) => {
  const origin = req.headers.origin
  if (!origin || !allowOrigins.includes(origin)) {
    next()
    return
  }

  // 預檢請求
  if (req.method === 'OPTIONS') {
    res.header('access-control-allow-methods', req.headers['access-control-request-method'])
    res.header('access-control-allow-headers', req.headers['access-control-request-headers'])
    // if (maxAge) res.header('access-control-max-age', maxAge)
  }

  // 附帶身份憑證的請求
  res.header('access-control-allow-credentials', true)

  // 簡單請求
  res.header('access-control-allow-origin', origin)
  next()
}
const express = require('express')

const router = express.Router()

router.get('/getCookie', (req, res) => {
  const value = 123456
  res.cookie('token', value, {
    path: '/',
    domain: 'localhost',
    maxAge: 7 * 24 * 3600 * 1000, //毫秒數
  })
  res.header("authorization", value).send()
})

router.get('/getData', (req, res) => {
  console.log(222)
  const data = JSON.stringify({
    name: '小明'
  })
  res.send(data)
})

module.exports = router

補充

在跨域訪問時,JS只能拿到一些最基本的響應頭,如:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要訪問其他頭,則需要伺服器設定本響應頭。Access-Control-Expose-Headers頭讓伺服器把允許瀏覽器訪問的頭放入白名單,例如:

Access-Control-Expose-Headers: authorization, a, b

相關文章