前后端都需要知道的跨域

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跨域请求,各个版本的跨域设置看文章底部。

{
    <!--必须 设置允许跨域的 origin -->
    "Access-Control-Allow-Origin": string
    <!--可选 设置允许跨域请求带的 requset header -->
    "Access-Control-Allow-Headers": String
    <!--可选 设置允许跨域的 Methods -->
    "Access-Control-Allow-Methods": String
    <!--可选 设置允许跨域的 cookie, 开启此字段,前端 ajax 需要 设置withCredentials 且 Access-Control-Allow-Origin 不能为 *  -->
    "Access-Control-Allow-Credentials": Boolean  
    <!--可选 设置option预检请求的缓存时间 -->
    "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: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
access-control-request-headers: x-lang,x-ostype
access-control-request-method: GET
origin: https://www.kaixindou.net
user-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: true
access-control-allow-headers: x-lang, x-ostype
access-control-allow-methods: OPTIONS,GET,POST
access-control-allow-origin: https://www.kaixindou.net
content-length: 0
date: Wed, 15 Aug 2018 13:09:38 GMT
server: nginx
status: 200
vary: Access-Control-Request-Method
vary: Origin
vary: 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.
    复制代码
    // 网络请求的 response
    Content-Type: application/json; charset=utf-8
    复制代码
    • origin 报错
    // console 的报错
    Origin xxxxxxxxxx is not allowed by Access-Control-Allow-Origin.
    复制代码
    // 网络请求的 response
    Content-Type: application/json; charset=utf-8
    复制代码
怎么避免这个预请求
  • 我们一般开发,会涉及到预请求的无非就是设置了其他的 header,方法我们也只是用到 get 和 post
  1. 让服务端缓存我们的预请求
// 让服务器设置下面这个头,在多次请求的时候可以避免二次预检
Access-Control-Max-Age: ms
复制代码
  1. 和服务端协商,设置的 header 使用 Last-Event-ID 等浏览器预置的 header,从根本上避免 option 的预请求
开发中遇到的问题
  • jq的 ajax 的携带 header 好坑,怎么都带不上去,建议使用 axios
    // 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
    }
}
复制代码
参考链接
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值