一般前后端项目中跨域解决
- 开发环境中直接vite, webpack 正向代理, 他们内置了一个简单的node server
- 生产环境直接部署到和和后端接口相同的域名下
- 如果调用其他不同域名下的请求呢?
- 传统jsonp,利用script的跨域, 常见的就是百度搜索联想词的使用,可以调用百度接口
- nginx反向代理
- bff 转发,也类似反向代理
- cors 响应头设置,需要对方服务器配合
electron中跨域的处理
跨域是浏览器端的问题, 可以直接用node发起请求, 避免跨域问题. 当然也有一些弊端
- bug排查,需要打印大量日志排查, 不如Chrome中的网络控制台直观,中间多了一层那么排查的时候就多了一个流程
- 中间转发有性能损伤,其次需要考虑更多的情况,比如有个get请求,但参数直接写在了body上. 这种情况很多时候无法考虑周到
- 还有证书错误的问题, nodejs中需要自己去忽略证书错误,但浏览器正常
所以建议请求放在前端页面去发起, electron中有以下几种方式解决跨域
- 首先file协议不存在跨域
- 使用webPreferences禁用 Web 安全策略,但这种方式已经不推荐了
- 渲染进程请求ipc,主进程发起请求 ,也不推荐,繁琐
- session模块修改cors请求头. 推荐 chrome中也可以看到网络监控
源码如下
const filter = {
urls: [
'https://主机名1.com/*',
'https://主机名2.cn/*',
]
}
// session.defaultSession.webRequest.onBeforeSendHeaders((details,callback) => {
// details.requestHeaders['Origin'] = '*'
// callback({ requestHeaders:details.requestHeaders});
// })
// 之前看到有人这样写,其实cors只和响应有关,无需设置请求Origin,注意filter后要带*
session.defaultSession.webRequest.onHeadersReceived(filter, (details, callback) => {
details.responseHeaders!['Access-Control-Allow-Origin'] = ['*']
callback({
cancel: false,
responseHeaders: details.responseHeaders
})
})
但仅仅以上就可以了吗,还不够,跨域三剑客, origin,method,cookie都需要设置
session.defaultSession.webRequest.onHeadersReceived(sessionFilter, (details, callback) => {
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
details.responseHeaders!['Access-Control-Allow-Origin'] = ['http://localhost:8081']
// 'http://localhost:8081',
// The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
details.responseHeaders!['Access-Control-Allow-Credentials'] = ['true']
details.responseHeaders!['Access-Control-Allow-Methods'] = ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE']
callback({
cancel: false,
responseHeaders: details.responseHeaders,
})
})
如果需要携带cookie, 请求的时候也要设置
fetch('https://主机名1.cn/api/v1/test',{credentials:'include'})
electron中设置cookie
// 跨域token的设置
// 方法一
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
const token = xxx.token
logger.info('cookies', details.requestHeaders['Cookie'])
details.requestHeaders['Cookie'] = `token=${token}`
callback({ requestHeaders: details.requestHeaders })
})
// 方法二
export const setTokenToCookie = (cookie:string) =>{
const parseCk = parseCookie(cookie)
const maxAge = parseInt(parseCk['Max-Age'])
const expirationDate = Math.round(Date.now() / 1000) + maxAge
const token_value = parseCk['token']
sessionFilter.urls.forEach(url => {
const origin = new URL(url).origin
const name = 'ulp_token'
session.defaultSession.cookies.remove(origin, name)
const setCookie: CookiesSetDetails = {
url: origin,// cookie domain
name,
value: token_value,
sameSite: 'no_restriction',//None , and secure must be true
httpOnly: false,
secure: true,
expirationDate,
}
session.defaultSession.cookies.set(setCookie)
})
}
// electron net.fetch 鉴权获取cookie
export const getUserInfoAndCookie = async (token: string) => {
const response = await net.fetch(`https://xx/login?token=${token}`, { method: 'get' })
const userInfo = await response.json()
const cookies = response.headers.getSetCookie()
const cookie = cookies?.find(ck => ck.includes('token'))
return { userInfo,tokenCookie:cookie }
}
补充知识
Set-Cookie的值
developer.mozilla.org/zh-CN/docs/…
1. Expires=<date> 可选
以 HTTP 日期时间戳形式指定的 cookie 的最长有效时间 如果没有指定,那么会是一个会话期 cookie。会话在客户端被关闭时结束,这意味着会话期 cookie 会在彼时被移除。 如果设置了 Expires 日期,其截止时间与客户端相关,而非服务器的时间。
2. Max-Age=<number> 可选
在 cookie 过期之前需要经过的秒数。秒数为 0 或负值将会使 cookie 立刻过期。假如同时设置了 Expires 和 Max-Age 属性,那么 Max-Age 的优先级更高
Expires和Max-Age单位都是秒,前者绝对单位,后者相对单位,更灵活
Date.now,data.getTime
js获取时间戳,单位都为毫秒,Date.now,data.getTime
Date.now()适用于直接获取当前时间的场景,
而getTime()适用于获取特定日期对象的时间戳。
Date.now()相对更简单和直观,而getTime()需要先创建一个Date对象。
3. SameSite=<samesite-value> 可选
控制 cookie 是否随跨站请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)
Strict
这意味浏览器仅对同一站点的请求发送 cookie,即请求来自设置 cookie 的站点。如果请求来自不同的域名或协议(即使是相同域名),则携带有 SameSite=Strict 属性的 cookie 不会被发送。
Lax
这意味着 cookie 不会在跨站请求中被发送,如:加载图像或框架(frame)的请求。但 cookie 在用户从外部站点导航到源站时,cookie 也会被发送(例如,访问一个链接)。这是 SameSite 属性未被设置时的默认行为。
None
这意味着浏览器在跨站和同站请求中均会发送 cookie。在设置这一属性值时,必须同时设置 Secure 属性,就像这样:SameSite=None; Secure。如果未设置 Secure,则会出现以下错误:
Cookie "myCookie" rejected because it has the "SameSite=None" attribute but is missing the "secure" attribute.
4. Secure 可选
表示仅当请求通过 https: 协议(localhost 不受此限制)发送时才会将该 cookie 发送到服务器,因此其更能够抵抗中间人攻击。
SameSite=None
Secure 必须为true
SameSite 浏览器控制台无法修改
5. HttpOnly 可选
阻止 JavaScript 通过 Document.cookie 属性访问 cookie。注意,设置了 HttpOnly 的 cookie 仍然会通过 JavaScript 发起的请求发送。例如,调用 XMLHttpRequest.send() 或 fetch()
Domain=<domain-value> 可选
指定 cookie 可以送达的主机。
只能将值设置为当前域名或更高级别的域名(除非是公共后缀)。设置域名将会使 cookie 对指定的域名及其所有子域名可用。
若缺省,则此属性默认为当前文档 URL 的主机(不包括子域名)。
为了保证安全性,cookie无法设置除当前域名或者其父域名之外的其他domain
列入 CORS 白名单的标头还必须满足以下要求: Content-Type 的 MIME 类型的解析值(忽略参数)需要是 application/x-www-form-urlencoded、multipart/form-data 和 text/plain 中的一个。
如果不满足 服务器需要设置Access-Control-Allow-Headers Content-Type eg post 跨域请求 传递json参数 Content-Type:application/json,此刻需要单独设置