what 跨域
- 跨域(跨域 HTTP 请求)就是前端在访问接口的时候,与访问的接口的 协议、域名、端口号 不是同一个,就会有跨域问题,这里产生跨域问题的原因是 XmlHttpRequest同源策略 ,也就是禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
why 跨域
- AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,因为我们发起的每一次HTTP请求都会带上请求地址对应的cookie。
请求的种类
1、简单请求
请求方法 | header |
HEAD | Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain) |
GET | |
POST |
2、非简单请求
除了上面的请求方法外的其他方法 delete、put 两个,还有携带其他自定义的请求头 即为非简单请求。
浏览器处理简单请求
1、基本流程
先增加一个 Origin 字段,这个是直接拿的请求发起页面的http协议和域名还有端口
服务端需要支持我们这个origin跨域请求,各个版本的跨域设置看文章底部。
{ "Access-Control-Allow-Origin": string "Access-Control-Allow-Headers": String "Access-Control-Allow-Methods": String "Access-Control-Allow-Credentials": Boolean "Access-Control-Max-Age": Number}复制代码
浏览器处理复杂请求
- 复杂请求在跨域请求的时候浏览器会发送一个预检请求,method 为 option,服务端需要首先通过验证这个预检请求,给这个请求返回 200 ,此时的 header 和 method 必须和
access-control-allow
中设置的保持一致。浏览器收到 option 响应,确认设置的 method 和 header 对上了,认为预检通过了,就开始正式的发送这个请求。
// 一次option预请求:authority: i-863.kaixindou.net:method: OPTIONS:path: /gameInfo/fetchPkWinStreakShareInfo?uid=101001638&streakWin=3:scheme: httpsaccept: */*accept-encoding: gzip, deflate, braccept-language: zh-CN,zh;q=0.9access-control-request-headers: x-lang,x-ostypeaccess-control-request-method: GETorigin: https://www.kaixindou.netuser-agent: Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36复制代码
- 预检请求的允许通过,此时预检请求返回 200 在
preview
中看到 200 或者一个空页面
access-control-allow-credentials: trueaccess-control-allow-headers: x-lang, x-ostypeaccess-control-allow-methods: OPTIONS,GET,POSTaccess-control-allow-origin: https://www.kaixindou.netcontent-length: 0date: Wed, 15 Aug 2018 13:09:38 GMTserver: nginxstatus: 200vary: Access-Control-Request-Methodvary: Originvary: Access-Control-Request-Headers复制代码
- 预检请求不允许通过,预检请求同样返回 200 ,在
preview
中看到 nothing to preview,response没有返回access-control-allow
相关字段- header 报错
// console 的报错Request header field xxxx is not allowed by Access-Control-Allow-Headers in preflight response.复制代码
// 网络请求的 responseContent-Type: application/json; charset=utf-8复制代码
- origin 报错
// console 的报错Origin xxxxxxxxxx is not allowed by Access-Control-Allow-Origin.复制代码
// 网络请求的 responseContent-Type: application/json; charset=utf-8复制代码
怎么避免这个预请求
- 我们一般开发,会涉及到预请求的无非就是设置了其他的 header,方法我们也只是用到 get 和 post
- 让服务端缓存我们的预请求
// 让服务器设置下面这个头,在多次请求的时候可以避免二次预检Access-Control-Max-Age: ms复制代码
- 和服务端协商,设置的 header 使用 Last-Event-ID 等浏览器预置的 header,从根本上避免 option 的预请求
开发中遇到的问题
- jq的 ajax 的携带 header 好坑,怎么都带不上去,建议使用
// axios比较烦的 get 请求和 post 请求的参数位不一样,建议使用自己简单封装下Axios({ url: url[region] + '/wemeet/set_img_status', method: 'post', data: data,// post 使用 param: data,// get 使用 headers: { 'X-Auth-Token': encodeURIComponent(token) }}).then(rsp => rsp.data).catch(err => { console.log(err.message)})复制代码
- 封装过的
export const axios = (url, type = 'GET', data = {}) => { if (typeof url === 'object') { return Axios(url) } if (typeof type === 'object') { data = type type = 'GET' } const axiosData = { withCredentials: true, method: type, url } if (type === 'GET') { axiosData.params = data } if (type === 'POST') { axiosData.data = data } axiosData.headers = { header: '12321' } return Axios(`${url}`, axiosData).then(rsp => { console.log(`${url}`, rsp.data) return rsp.data }).catch(e => { alert(e.message) })}复制代码
附录
- node express 跨域设置
// 使用express中间件const whiteList = [ xx.com ]app.use('*', (req, res, next) => { let origin = req.headers.origin let allowOrigin = 'http://fe.xx.com' origin && whiteList.forEach(v => { if (origin.indexOf(v) !== -1) { allowOrigin = origin } }) res.header("Access-Control-Allow-Origin", allowOrigin) res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS") res.header("Access-Control-Allow-Credentials", true) res.header("Access-Control-Max-Age", 1728000) res.header("X-Powered-By", '3.2.1') if (req.method == 'OPTIONS') { res.send(200) } else { next() }});复制代码
- go 跨域设置
func allowCrossOrigin(rw http.ResponseWriter, req *http.Request) { if strings.ContainsAny(req.Header.Get("Origin"), "yy.com") { rw.Header().Set("Access-Control-Allow-Origin", req.Header.Get("Origin")) rw.Header().Set("Access-Control-Allow-Method", "PUT,OPTIONS,POST,GET") rw.Header().Set("Access-Control-Allow-Credentials", "true") rw.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Auth-Token") }}func HttpGetImgList(rw http.ResponseWriter, req *http.Request) { if req.Method == http.MethodOptions { allowCrossOrigin(rw, req) com_http.SendCommonResp(rw, req.URL.Query().Get("callback"), 0, "r u ok", "") return }}复制代码