设置cookie
golang 跨子域设置cookie
参考文档: https://golang.0voice.com/?id=9784
在使用 Golang 设置跨子域的 Cookie 时,你需要确保以下步骤:
- 在服务器端设置允许跨域请求,包括设置适当的 CORS(Cross-Origin Resource Sharing)响应头。可以通过
Access-Control-Allow-Origin
来指定允许跨域请求的源。 - 在设置 Cookie 时,确保指定了
Domain
字段为主域名以及子域名的共享父级域名。这样可以使得该 Cookie 能够被多个子域名所共享。
下面是一个示例代码片段,展示了如何在 Golang 中进行跨子域设置 Cookie:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
// 设置允许跨域请求
w.Header().Set("Access-Control-Allow-Origin", "http://example.com") // 替换成实际允许的源
// 设置 Cookie
cookie := http.Cookie{
Name: "my-cookie",
Value: "cookie-value",
Path: "/",
Domain: ".example.com", // 替换成实际的共享父级域名
HttpOnly: true,
}
http.SetCookie(w, &cookie)
w.Write([]byte("Cookie set successfully"))
}
请注意替换示例中的实际允许的源和共享父级域名。此外,还可以根据需要设置其他 Cookie 属性,例如 MaxAge
、Secure
等。
Domain 设置有校验
只有有效的domain才会设置到cookie中:
net/http/cookie.go
// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func SetCookie(w ResponseWriter, cookie *Cookie) {
if v := cookie.String(); v != "" {
w.Header().Add("Set-Cookie", v)
}
}
在cookie.String()
会校验Domain:
// String returns the serialization of the cookie for use in a Cookie
// header (if only Name and Value are set) or a Set-Cookie response
// header (if other fields are set).
// If c is nil or c.Name is invalid, the empty string is returned.
func (c *Cookie) String() string {
if c == nil || !isCookieNameValid(c.Name) {
return ""
}
// extraCookieLength derived from typical length of cookie attributes
// see RFC 6265 Sec 4.1.
const extraCookieLength = 110
var b strings.Builder
b.Grow(len(c.Name) + len(c.Value) + len(c.Domain) + len(c.Path) + extraCookieLength)
b.WriteString(c.Name)
b.WriteRune('=')
b.WriteString(sanitizeCookieValue(c.Value))
if len(c.Path) > 0 {
b.WriteString("; Path=")
b.WriteString(sanitizeCookiePath(c.Path))
}
if len(c.Domain) > 0 {
if validCookieDomain(c.Domain) {
// A c.Domain containing illegal characters is not
// sanitized but simply dropped which turns the cookie
// into a host-only cookie. A leading dot is okay
// but won't be sent.
d := c.Domain
if d[0] == '.' {
d = d[1:]
}
b.WriteString("; Domain=")
b.WriteString(d)
} else {
log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain)
}
}
}
validCookieDomain(c.Domain)
的校验实现:
// validCookieDomain reports whether v is a valid cookie domain-value.
func validCookieDomain(v string) bool {
if isCookieDomainName(v) {
return true
}
if net.ParseIP(v) != nil && !strings.Contains(v, ":") {
return true
}
return false
}
// isCookieDomainName reports whether s is a valid domain name or a valid
// domain name with a leading dot '.'. It is almost a direct copy of
// package net's isDomainName.
func isCookieDomainName(s string) bool {
if len(s) == 0 {
return false
}
if len(s) > 255 {
return false
}
if s[0] == '.' {
// A cookie a domain attribute may start with a leading dot.
s = s[1:]
}
last := byte('.')
ok := false // Ok once we've seen a letter.
partlen := 0
for i := 0; i < len(s); i++ {
c := s[i]
switch {
default:
return false
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
// No '_' allowed here (in contrast to package net).
ok = true
partlen++
case '0' <= c && c <= '9':
// fine
partlen++
case c == '-':
// Byte before dash cannot be dot.
if last == '.' {
return false
}
partlen++
case c == '.':
// Byte before dot cannot be dot, dash.
if last == '.' || last == '-' {
return false
}
if partlen > 63 || partlen == 0 {
return false
}
partlen = 0
}
last = c
}
if last == '-' || partlen > 63 {
return false
}
return ok
}
说明:
.wl_dev.com
不是一个有效的Domain。
测试代码:
package main
import (
"fmt"
"net"
)
func isCookieDomainName(s string) bool {
if len(s) == 0 {
return false
}
if len(s) > 255 {
return false
}
if s[0] == '.' {
// A cookie a domain attribute may start with a leading dot.
s = s[1:]
}
last := byte('.')
ok := false // Ok once we've seen a letter.
partlen := 0
for i := 0; i < len(s); i++ {
c := s[i]
switch {
default:
return false
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
// No '_' allowed here (in contrast to package net).
ok = true
partlen++
case '0' <= c && c <= '9':
// fine
partlen++
case c == '-':
// Byte before dash cannot be dot.
if last == '.' {
return false
}
partlen++
case c == '.':
// Byte before dot cannot be dot, dash.
if last == '.' || last == '-' {
return false
}
if partlen > 63 || partlen == 0 {
return false
}
partlen = 0
}
last = c
}
if last == '-' || partlen > 63 {
return false
}
return ok
}
func main() {
fmt.Println(isCookieDomainName(".wldev.com"))
fmt.Println(isCookieDomainName(".wl119.com"))
fmt.Println(isCookieDomainName(".wl_dev.com"))
fmt.Println(net.ParseIP("wl_dev.com"))
fmt.Println(net.ParseIP("wl119.club"))
}
输出:
true
true
false
<nil>
<nil>
Domain Matching
参考文档: https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
Domain的匹配规则:
A string domain-matches a given domain string if at least one of the following conditions hold:
-
The domain string and the string are identical. (Note that both the domain string and the string will have been canonicalized to lower case at this point.)
-
All of the following conditions hold:
- The domain string is a suffix of the string.
- The last character of the string that is not included in the domain string is a %x2E (".") character.
- The string is a host name (i.e., not an IP address).
http请求什么时候会带上cookie信息
参考文档: https://blog.csdn.net/john1337/article/details/104571244
Set-Cookie响应头字段(Response header):是服务器发送到浏览器或者其他客户端的一些信息,一般用于登陆成功的情况下返回给客户端的凭证信息,然后下次请求时会带上这个cookie,这样服务器端就能知道是来自哪个用户的请求了。
Cookie请求头字段是客户端发送请求到服务器端时发送的信息(满足一定条件下浏览器自动完成,无需前端代码辅助)。
下表为Set-Cookie响应头可以设置的属性
NAME=VALUE | 赋予 Cookie 的名称和其值(必需项) |
---|---|
expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则默 认为文档所在的文件目录) |
domain=域名 | 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie 的服务器的域名) |
Secure | 仅在 HTTPS 安全通信时才会发送 Cookie |
HttpOnly | 加以限制, 使 Cookie 不能被 JavaScript 脚本访问 |
请看上面标红的三个属性(path, domain, Secure), 拿一个Http POST请求来说 http://aaa.www.com/xxxxx/list
如果满足下面几个条件:
- 浏览器端某个Cookie的domain字段等于aaa.www.com或者www.com
- 都是http或者https,或者不同的情况下Secure属性为false
- 要发送请求的路径,即上面的xxxxx跟浏览器端Cookie的path属性必须一致,或者是浏览器端Cookie的path的子目录,比如浏览器端Cookie的path为/test,那么xxxx必须为/test或者/test/xxxx等子目录才可以
注: 上面3个条件必须同时满足,否则该Post请求就不能自动带上浏览器端已存在的Cookie