[zeromicro/go-zero]关于跨域自定义header的问题

2023-12-19 696 views
1
场景说明:

在前后端分离的开发中,前端是单独部署的,可能是一个www.aaa.com的域名,而用go-zero的后端程序,可能部署在了www.bbb.com,这种方式在处理用户登陆的时候,基本上用的是jwt,用到jwt 基本上就要用到自定义header 的问题,比如header 里面可能会传Authorization这样的header头,但是Authorization这个又不是标准的响应头具体可以参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS 然后看了一下官方的关于跨域的请求方式

package rest

import "net/http"

const (
    allowOrigin  = "Access-Control-Allow-Origin"
    allOrigins   = "*"
    allowMethods = "Access-Control-Allow-Methods"
    allowHeaders = "Access-Control-Allow-Headers"
    headers      = "Content-Type, Content-Length, Origin"
    methods      = "GET, HEAD, POST, PATCH, PUT, DELETE"
)

// CorsHandler handles cross domain OPTIONS requests.
// At most one origin can be specified, other origins are ignored if given.
func CorsHandler(origins ...string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if len(origins) > 0 {
            w.Header().Set(allowOrigin, origins[0])
        } else {
            w.Header().Set(allowOrigin, allOrigins)
        }
        w.Header().Set(allowMethods, methods)
        w.Header().Set(allowHeaders, headers)
        w.WriteHeader(http.StatusNoContent)
    })
}

这个写得很简单,属于 简单跨域请求,满足不了,

前后端分离的复杂跨域请求(因为是要做自定义header)

回答

5

为了解决这个问题,我是重新设计了跨域的中间件:

// api/internal/middleware/corsmiddleware.go

package middleware

import "net/http"

// CorsMiddleware 跨域请求处理中间件
type CorsMiddleware struct {
}

// NewCorsMiddleware 新建跨域请求处理中间件
func NewCorsMiddleware() *CorsMiddleware {
    return &CorsMiddleware{}
}

// Handle 跨域请求处理
func (m *CorsMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        setHeader(w)

        // 放行所有 OPTIONS 方法
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusNoContent)
            return
        }

        // 处理请求
        next(w, r)
    }
}

// Handler 跨域请求处理器
func (m *CorsMiddleware) Handler() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        setHeader(w)

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusNoContent)
        } else {
            w.WriteHeader(http.StatusNotFound)
        }
    })
}

// setHeader 设置响应头
func setHeader(w http.ResponseWriter) {
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, Authorization, AccessToken, Token")
    w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
    w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
    w.Header().Set("Access-Control-Allow-Credentials", "true")
}

在main函数中:

// api/api.go

func main() {
        ......
    server := rest.MustNewServer(c.RestConf,
        rest.WithNotAllowedHandler(middleware.NewCorsMiddleware().Handler()))
    defer server.Stop()
    server.Use(middleware.NewCorsMiddleware().Handle)
        ......
}

重点是要分别注册MethodNotAllowed的情况下和MethodAlowed情况下的跨域处理。

2

我觉得吧,跨域这事应该在nginx上做的

7

感谢 @silveryou

现在跨域可以这么写,从 v1.2.3 开始

srv := rest.MustNewServer(c, rest.WithCors())

如果单个域名可以这么写:

srv := rest.MustNewServer(c, rest.WithCors("http://example.com"))
3

srv := rest.MustNewServer(c, rest.WithCors()) 依然会报跨域错误

2

headers = "Content-Type, Content-Length, Origin" 这个里面,加入客户端请求自定义的header ,如 "token,my-token,"看请求里有哪些自定义的,都写上,应该就没问题 如 headers = "Content-Type, Content-Length, Origin,token,my-token"

7

var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, }

在Websocket实例中配置下CheckOrigin试试