浏览器跨域
同源策略
同源策略是浏览器的行为,是为了保护本地数据不被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