同源策略
同源策略是瀏覽器的行為,是為了保護本地資料不被JavaScript程式碼獲取回來的資料汙染,因此攔截的是客戶端發出的請求返回的資料接收,即請求傳送了,伺服器響應了,但是瀏覽器不接收。
同源:請求要滿足協議,域名,埠都相同
http://www.test:8080/api 和 http://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