wordpress大前端d8主题,seo推广工具,哈尔滨公告最新消息,网站厨师短期培训班一、同源策略
同源策略是一个重要的安全策略#xff0c;它用于限制一个Origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档#xff0c;减少可能被攻击的媒介。
Origin#xff1a;指web文档的来源#xff0c;Web 内容的来源取决于访问的U…一、同源策略
同源策略是一个重要的安全策略它用于限制一个Origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档减少可能被攻击的媒介。
Origin指web文档的来源Web 内容的来源取决于访问的URL的方案 (协议)主机 (域名) 和端口定义。只有当方案主机和端口都匹配时两个对象具有相同的起源。 二、跨域
关于URL是否同源根据上图中的①②③进行判断即可只要有一点不同就达到跨域的条件。顺带一提即便是向域名对应的ip进行资源请求仍然会跨域。
IE的特殊性Internet Explorer 的同源策略有两点差异一是IE未将端口号纳入同源策略的检查其次是两个高度互信的域名也不受同源策略的检查。
常见的跨域情景 浏览器内常见的跨域报错 跨域出现的场景
一般常见于开发阶段本地启动项目后当前页面域名和后台服务器域名不相同导致跨域。在项目上线后会通过统一域名、后端配置域名白名单等方式避免跨域。
下方的解决方案中我们通过koa2框架搭建服务器实现一系列的情景模拟。
三、跨域的解决方案
1.JSONP
原理通过script标签没有跨域限制的特性进行资源的请求和获取。
限制需要目标服务器进行配合且仅支持get请求
我们直接通过代码和注释理解jsonp的使用前端代码如下 script window.jsonp function(res){ console.log(res); } /script script srchttp://localhost:9527/jsonp?val123cbjsonp/script 后端代码如下
// 定义jsonp接口
router.get(/jsonp, async (ctx, next) {
/*
1.后端通过query获取前端传来的请求参数
其中包括
· 交予后端进行功能逻辑操作的数据如val
· 交予后端进行jsonp操作的函数名如cb
*/ const {cb, val} ctx.query // 2.调用回调函数进行传参将处理好的数据返回给前端 if(val 123){ const requestData { code: 10001, data: 登陆成功 } //在响应体中触发目标函数并将处理好的数据requestData作为实参传入 ctx.body ${cb}(${JSON.stringify(requestData)}); } }) 前端通过window对象在全局挂载了一个待触发的函数。 后端通过响应体触发这个函数并将数据作为入参传给前端。 了解简单的实现后前端可以对jsonp的功能再进行一层封装 /* 1. 生成script标签我们需要script标签进行接口的调用 2. 处理参数数据分别整理好接口接口参数函数名等数据并进行填充 3. 写入生成好的script标签实现接口的调用(返回promise对象便于链式调用) 4. 清除script标签 */ function jsonp(requestData) { // 对传入参数进行处理 const { url, data, jsonp } requestData; let query ; for (let key in data) { query ${key}${data[key]}; } const src ${url}?${query}jsonp${jsonp}; // 生成、填充script标签在页面中挂载调用接口 let scriptTag document.createElement(script); scriptTag.src src; document.body.appendChild(scriptTag); return new Promise((resolve, reject) { window[jsonp] function(rest){ resolve(rest) document.body.removeChild(scriptTag) } }) } // 整理数据 const requestData { url: http://localhost:9527/jsonp, data: { val: 123, }, jsonp: getMessage } // 接口调用 btn.onclick function () { jsonp(requestData).then(function (response) { console.log(response); }) } 2.CORS
Cross-Origin Resource sharing跨域资源共享是一种基于HTTP头的机制该机制允许服务器标示除了它自己以外其他origin域名协议和端口既浏览器在跨域的情景下仍然能从目标服务器请求并获取资源。
而对服务器数据可能产生副作用的HTTP请求方法都会触发CORS中的预检机制。
CORS中通过预检机制(preflight request)检查服务器是否允许浏览器发送真实请求浏览器会先发送一个预检请求(option请求)请求中会携带真实请求的请求信息
origin请求的来源
Access-Control-Request-Method
通知服务器在真正的请求中会采用哪种HTTP方法GET,POST,DELETE...
Access-Control-Request-Headers通知服务器在真正的请求中会采用哪些请求头
服务器可以在预检请求中可以根据以上三条信息确定预检请求是否通过
//server.js
app.use(async (ctx, next) {
// 允许跨域资源共享的白名单
const whiteList [http://127.0.0.1:5500]
// 判断目标源是否通行
const pass whiteList.includes(ctx.header.origin)
// 对于预检请求如果没有设置正确的响应状态浏览器会直接拦截真实请求直接报错提示跨域
// 所以我们可以在这一部分确定客户端的请求是否符合我们的要求
if (ctx.method OPTIONS) {
if (!pass) return
// 预检放行
ctx.status 204
}
await next();
});
响应的状态码是决定预检请求是否通过的关键返回正常的状态码通常是204就能通过预检请求让浏览器发出真实的请求。
在代码中也可以看出pass是决定预检请求的关键那在实际的项目中还得根据设计去决定通行的具体条件。当通过预检请求后后台可以设置对应的响应头数据例如是否允许目标源跨域资源共享
//server.js
app.use(async (ctx, next) {
console.log(middleware for cors);
// 允许跨域资源共享的白名单
const whiteList [http://127.0.0.1:5500]
// 判断目标源是否通行
const pass whiteList.includes(ctx.header.origin)
// 对于预检请求如果没有设置正确的响应状态浏览器会直接拦截真实请求直接报错跨域
// 所以我们可以在这一部分确定客户端的请求是否符合我们的要求
if (ctx.method OPTIONS) {
if (!pass) return
// 预检放行
ctx.status 204
}
// 允许访问的origin
ctx.set(Access-Control-Allow-Origin, ctx.headers.origin);
// cookie是否允许携带
ctx.set(Access-Control-Allow-Credentials, true);
// 允许访问的HTTP方法
ctx.set(Access-Control-Request-Method, PUT,POST,GET,DELETE,OPTIONS);
// 哪些请求头允许通行
ctx.set(
Access-Control-Allow-Headers,
X-Requested-With,Content-Type,Accept,Origin
);
// 暴露给客户端的响应头信息在不设置的情况下客户端只能获取默认的响应头如’content-type‘
ctx.set(
Access-Control-Expose-Headers,
With-Requested-Key
);
// 设置对应的响应头数据
ctx.set(
With-Requested-Key,
HW
);
// 预检结果的缓存时间毫秒为单位Firefox上限是86400-24小时Chromium(谷歌引擎)上限是7200-2小时
ctx.set(Access-Control-Max-Age, 0);
await next();
});
其中需要注意两个点
关于Access-Control-Expose-Header
使用CORS时浏览器只允许获取默认的响应头像上文代码中的标头With-Requested-Key即便我们可以通过浏览器的调试器查看也无法通过代码去获取这时候就需要后台通过Access-Control-Expose-Header进行暴露后台代码在已在上方统一贴出。
前端代码
body
button idbtn 请求资源 /button
/body
script
btn.onclick function () {
axios.post(http://localhost:9527/getMessage, {
firstName: Fred,
lastName: Flintstone
})
.then(function (response) {
// 可以在里面查找到暴露出来的响应头数据如’With-Requested-Key‘: HW
console.log(response.headers);
})
.catch(function (error) {
console.log(error);
});
}
/script
关于Access-Control-Allow-Credentials
使用CORS时默认不携带cookie需要同时满足三个条件才能在使用CORS时进行cookie的传递:
浏览器的请求中设置withCredentials参数为true
服务端设置标头Access-Control-Allow-Credentials为true
服务端设置标头Access-Control-Allow-Origin不为*
我们可以在原生ajax请求中设置该参数或者在axios的默认配置中设置该参数
// 原生ajax
const xhr new XMLHttpRequest()
xhr.withCredentials true
// axios
axios.defaults.withCredentials true;
Ok明白CORS的作用以及明白CORS中的预检机制后接下来是了解什么时机下会触发预检机制。
CORS中归纳了一系列不会触发预检机制的请求场景即满足所有下述条件的情况下统称为简单请求
使用这三种方法之一GET HEAD POST
不得人为设置此集合外的其他首部字段Accept Accept-Language Content-Language Content-Type
Content-type的值仅限于这三者之一
text/plain
multipart/form-data
application/x-www/form-urlencoded
请求中XMLHttpRequest实例没有注册任何事件监听器即XMLHttpRequest实例对象可以使用XMLHttpRequest.upload属性进行访问
请求中没有使用ReadableStream对象
小结CORS中主要区分了简单请求和复杂请求两种情况复杂请求会触发CORS的预检机制。通过上方的案例也可以清楚CORS的配置主要是在服务端但客户端也需要知道CORS的使用注意点例如响应头数据的获取以及cookies的携带配置这些知识应该是前后端都需要掌握的技能点。
3.服务器代理
同源策略主要是限制浏览器和服务器之间的请求服务器与服务器之间并不存在跨域。 我们可以通过koa2模拟和实现这种概念
//前端代码
body
button idbtn 请求资源 /button
script
btn.onclick function () {
let url checkUrlProxy(http://localhost:9527/api/getMessage,api)
axios.post(url, {
firstName: Fred,
lastName: Flintstone
})
.then(function (response) {
console.log(response);
})
}
// 判断接口是否携带api字段若是则更改为代理服务器对应的域名
function checkUrlProxy(url, proxyFlag){
let proxyServer http://localhost:1005
let urlArr [url.split(/)[1],url.split(/)[3]]
if(urlArr.includes(proxyFlag)) {
return ${proxyServer}/${proxyFlag}${url.split(proxyFlag)[1]}
}
return url
}
//
/script
/body
前端的代码部分通过checkUrlProxy函数简单地确定本次请求是否要转向代理服务器。
后端代码如下
//proxyServer.js
let requestFlag false
let body
app.use(async (ctx, next) {
// 全放行
if (ctx.method OPTIONS) {
ctx.status 204
requestFlag false
} else {
requestFlag true
}
ctx.set(Access-Control-Allow-Origin, *);
ctx.set(Access-Control-Allow-Credentials, true);
ctx.set(Access-Control-Request-Method, *);
ctx.set(
Access-Control-Allow-Headers,
X-Requested-With,Content-Type,Accept,Origin
);
ctx.set(Access-Control-Max-Age, 86400);
// 根据具体情况进行修改
ctx.set(Access-Control-Expose-Headers, With-Requested-Key);
await next();
if(requestFlag) {
ctx.body body
body
}
});
app.use(async (ctx, next) {
if (!requestFlag) return
await p4r(ctx)
});
function p4r(ctx) {
return new Promise((res, rej) {
const proxyRequest http.request({
host: 127.0.0.1,
port: 9527,
path: ctx.url,
method: ctx.method,
headers: ctx.header
},
serverResponse {
serverResponse.on(data, chunk {
body chunk
})
serverResponse.on(end, () {
res(body)
})
}
)
proxyRequest.end()
})
}
app.on(error, (err, ctx) {
console.error(server error, err, ctx)
});
app.listen(1005, (err) {
if (err) console.log(服务器启动失败);
else console.log(proxy server 1005 running -- ✨✨✨);
})
//targetServer.js
const data {val : 123}
// 配合代理服务器的post路由
router.post(/api/getMessage, (ctx) {
ctx.body JSON.stringify(data)
})
// 定义好路由组件的内容后进行路由注册
app.use(router.routes())
app.on(error, (err, ctx) {
console.error(server error, err, ctx)
});
app.listen(9527, (err) {
if (err) console.log(服务器启动失败);
else console.log(服务器启动成功);
})
后端代码主要分两部分
代理服务器(proxyServer)代理服务器设置CORS时不限制通行在koa2框架中通过中间件向目标服务器发送请求当接收到对应数据后再响应给浏览器
目标服务器(targetServer)目标服务器不需要做太复杂的配置案例中只是将数据传递给请求方
Ok我们通过这个案例明确代理服务器的具体效果浏览器向目标服务器直接请求资源仍然会受到同源策略的影响但通过代理服务器向目标服务器请求资源时却没这种限制。
那在实际项目中我们可以通过脚手架或打包工具的配置文件简洁方便地设置代理服务器无需自己手写服务器代码拿vue的脚手架为例
devServer:{
proxy:{
api:{
target:127.0.0.1:9527, //目标服务器地址
changeOrigin: true, // 是否允许跨域
pathRewrite: { //是否重写接口
api:,
}
}
}
}
在配置的时候可以通过框架的脚手架或者打包工具确定配置文件例如一些熟悉的字眼vue.config.jswebpack.config.jspackage.json(react),更准确的做法就是直接去对应工具的官方文档查阅代理服务器的配置介绍。