跨域携带cookie

177 阅读2分钟

相信有不少人在 web 开发工作过程中多多少少会遇到跨域限制问题,这里主要介绍一下如何设置让浏览器允许跨域请求,并同时携带 cookie 信息。

主要有2种场景:

  • 同根域名网站,比如 test1.example.com, test2.example.com 这两个域名,它们的一级域名都为 example.com
  • 不同根域名,比如 web1.com 和 web2.com

同根域名网站

  1. 假设有 test1.example.com, test2.example.com 域名

  2. 在 test1 前端用 fetch 请求 test2 时,需要设置 {crendentials: 'include'} 的配置参数

  3. test2 响应时需要设置以下头信息返回:

    • Access-Control-Allow-Credentials: true
    • Access-Control-Allow-Origin: https://test2.example.com

不同根域名网站

  1. 假设有 web1.com, web2.com 域名,需要在 web1.com 网站上请求 web2.com 接口数据

  2. 当 web2 设置 cookie 需要设置为 SameSite=None; Security, 具体原因如图所示:

    图片.png

    来源:developer.mozilla.org/zh-CN/docs/…

  3. cookie 的 security 属性要求了网站必须是 https 协议

  4. 当在 web1 前端用 fetch 请求 web2 时,需要设置 {crendentials: 'include'} 的配置参数

  5. web2 响应时需要设置以下头信息返回:

    • Access-Control-Allow-Credentials: true
    • Access-Control-Allow-Origin: https://web1.com
  6. 另外,笔者在 Chrome 浏览器测试是能正常工作,在 Firefox(Version 133.0) 验证一直 cookie 没法正常携带过去。最后通过修改 设置 -> 隐私与安全 -> 自定义 -> cookie -> 未访问网站的cookie ,再重新加载页面才能正常。(不知道 Firefox 是不是默认比 Chrome 严格还是什么其他原因,如果有谁知道可以帮忙解惑一下)

    图片.png

简单的验证用例

这里只提供一下“不同根域名网站”的验证场景

  1. web1 的页面
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web1</title>
  <script type="application/javascript">
    function fetchWeb2() {
      fetch('https://web2.com:8081/yoo', {
        method: 'POST',
        credentials: 'include',
      })
        .then(response => response.text())
        .then(data => {
          console.log('Web2 response:', data);
        })
        .catch(error => console.error('Error fetching web2:', error));
    }
  </script>
</head>
<body>
  <button onclick="fetchWeb2()">Fetch Web2</button>
</body>
</html>
  1. web1 的 server,使用 golang 提供一个简单的 https 服务
package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		b, err := os.ReadFile("web1/index.html")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Write(b)
	})

	fmt.Println("Server is running on port 8080")
	http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil)
}
  1. 证书通过以下命令简单生成
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
  1. web2 的 server 主要提供 setcooke 和 yoo 验证接口
package main

import (
	"fmt"
	"net/http"
)

func main() {

	http.HandleFunc("/yoo", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "https://web1.com:8080")
		w.Header().Set("Access-Control-Allow-Credentials", "true")

		cookie, err := r.Cookie("web2")
		if err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		w.Write([]byte("Hello, " + cookie.Value))
		return
	})

	http.HandleFunc("/setcookie", func(w http.ResponseWriter, r *http.Request) {
		cookie := http.Cookie{
			Name:     "web2",
			Value:    "web2",
			SameSite: http.SameSiteNoneMode,
			Secure:   true,
		}
		http.SetCookie(w, &cookie)
	})

	fmt.Println("Server is running on port 8081")
	http.ListenAndServeTLS(":8081", "cert.pem", "key.pem", nil)
}
  1. 绑定 hosts 文件后,在浏览器先请求 web2.com:8081/setcookie , 然后打开 web1.com:8080 页面,点击按钮发送请求。从下图可以看到在 web1 网站下成功请求了 web2, 并携带上 web2 自己的 cookie 信息。

    图片.png


好了,上述便是我这期分析的内容,希望对你有帮助,我们有缘再见