相信有不少人在 web 开发工作过程中多多少少会遇到跨域限制问题,这里主要介绍一下如何设置让浏览器允许跨域请求,并同时携带 cookie 信息。
主要有2种场景:
- 同根域名网站,比如 test1.example.com, test2.example.com 这两个域名,它们的一级域名都为 example.com
- 不同根域名,比如 web1.com 和 web2.com
同根域名网站
-
假设有 test1.example.com, test2.example.com 域名
-
在 test1 前端用 fetch 请求 test2 时,需要设置 {crendentials: 'include'} 的配置参数
-
test2 响应时需要设置以下头信息返回:
Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin: https://test2.example.com
不同根域名网站
-
假设有 web1.com, web2.com 域名,需要在 web1.com 网站上请求 web2.com 接口数据
-
当 web2 设置 cookie 需要设置为
SameSite=None; Security, 具体原因如图所示: -
cookie 的 security 属性要求了网站必须是 https 协议
-
当在 web1 前端用 fetch 请求 web2 时,需要设置 {crendentials: 'include'} 的配置参数
-
web2 响应时需要设置以下头信息返回:
Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin: https://web1.com
-
另外,笔者在 Chrome 浏览器测试是能正常工作,在 Firefox(Version 133.0) 验证一直 cookie 没法正常携带过去。最后通过修改 设置 -> 隐私与安全 -> 自定义 -> cookie -> 未访问网站的cookie ,再重新加载页面才能正常。(不知道 Firefox 是不是默认比 Chrome 严格还是什么其他原因,如果有谁知道可以帮忙解惑一下)
简单的验证用例
这里只提供一下“不同根域名网站”的验证场景
- 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>
- 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)
}
- 证书通过以下命令简单生成
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
- 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)
}
-
绑定 hosts 文件后,在浏览器先请求 web2.com:8081/setcookie , 然后打开 web1.com:8080 页面,点击按钮发送请求。从下图可以看到在 web1 网站下成功请求了 web2, 并携带上 web2 自己的 cookie 信息。
好了,上述便是我这期分析的内容,希望对你有帮助,我们有缘再见