1. 程式人生 > >帶cookie跨域問題的思路以及echo的解決方案

帶cookie跨域問題的思路以及echo的解決方案

urn ctu coo json 出現 stack white 思路 規則

問題起因

前後端分離,前端要訪問後端資源,而且需要攜帶cookie信息,這時碰到了跨域問題。一開始以為設置為允許跨域allow_origins為即可。可是瀏覽器還是攔截的請求,於是查看跨域規則,原來跨域allow_origins為時,只允許簡單的跨域,比如get,post,但是如果攜帶cookie,則會出現失敗。

思路

後來查看文檔,原來按照跨域請求的規則,當跨域和來源一致時才可以攜帶cookie。

echo框架中的解決辦法

有了思路就好辦了,echo框架中的跨域設置不夠詳細,做不到設置來源跨域。於是我修改了一下其中的跨域中間件,增加了一子域名的跨域。
實際上可以修改為,任意來源的跨域,但是這樣就不能保證安全了,不過如果是做接口平臺倒是可以這麽辦。

完整代碼為:

package middleware

import (
    "net/http"
    "strconv"
    "strings"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

type (
    // CORSConfig defines the config for CORS middleware.
    CORSConfig struct {
        // Skipper defines a function to skip middleware.
        Skipper middleware.Skipper

        // AllowOrigin defines a list of origins that may access the resource.
        // Optional. Default value []string{"*"}.
        AllowOrigins []string `json:"allow_origins"`

        // AllowMethods defines a list methods allowed when accessing the resource.
        // This is used in response to a preflight request.
        // Optional. Default value DefaultCORSConfig.AllowMethods.
        AllowMethods []string `json:"allow_methods"`

        // AllowHeaders defines a list of request headers that can be used when
        // making the actual request. This in response to a preflight request.
        // Optional. Default value []string{}.
        AllowHeaders []string `json:"allow_headers"`

        // AllowCredentials indicates whether or not the response to the request
        // can be exposed when the credentials flag is true. When used as part of
        // a response to a preflight request, this indicates whether or not the
        // actual request can be made using credentials.
        // Optional. Default value false.
        AllowCredentials bool `json:"allow_credentials"`

        // ExposeHeaders defines a whitelist headers that clients are allowed to
        // access.
        // Optional. Default value []string{}.
        ExposeHeaders []string `json:"expose_headers"`

        // MaxAge indicates how long (in seconds) the results of a preflight request
        // can be cached.
        // Optional. Default value 0.
        MaxAge int `json:"max_age"`
        AllowSubDomain bool
        MainDomain string
    }
)

var (
    // DefaultCORSConfig is the default CORS middleware config.
    DefaultCORSConfig = CORSConfig{
        Skipper:      middleware.DefaultSkipper,
        AllowOrigins: []string{"*"},
        AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
    }
)

// CORS returns a Cross-Origin Resource Sharing (CORS) middleware.
// See: https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS
func CORS() echo.MiddlewareFunc {
    return CORSWithConfig(DefaultCORSConfig)
}

// CORSWithConfig returns a CORS middleware with config.
// See: `CORS()`.
func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
    // Defaults
    if config.Skipper == nil {
        config.Skipper = DefaultCORSConfig.Skipper
    }
    if len(config.AllowOrigins) == 0 {
        config.AllowOrigins = DefaultCORSConfig.AllowOrigins
    }
    if len(config.AllowMethods) == 0 {
        config.AllowMethods = DefaultCORSConfig.AllowMethods
    }

    allowMethods := strings.Join(config.AllowMethods, ",")
    allowHeaders := strings.Join(config.AllowHeaders, ",")
    exposeHeaders := strings.Join(config.ExposeHeaders, ",")
    maxAge := strconv.Itoa(config.MaxAge)

    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if config.Skipper(c) {
                return next(c)
            }

            req := c.Request()
            res := c.Response()
            origin := req.Header.Get(echo.HeaderOrigin)
            allowOrigin := ""

            // Check allowed origins
            for _, o := range config.AllowOrigins {
                if o == "*" || o == origin {
                    allowOrigin = o
                    break
                }
            }
            if  config.AllowSubDomain  && config.MainDomain != ""{
                if strings.Contains(origin,config.MainDomain) {
                    allowOrigin = origin
                }
            }
            // Simple request
            if req.Method != echo.OPTIONS {
                res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
                res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
                if config.AllowCredentials {
                    res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
                }
                if exposeHeaders != "" {
                    res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders)
                }
                return next(c)
            }

            // Preflight request
            res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
            res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
            res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
            res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
            res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods)
            if config.AllowCredentials {
                res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
            }
            if allowHeaders != "" {
                res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders)
            } else {
                h := req.Header.Get(echo.HeaderAccessControlRequestHeaders)
                if h != "" {
                    res.Header().Set(echo.HeaderAccessControlAllowHeaders, h)
                }
            }
            if config.MaxAge > 0 {
                res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge)
            }
            return c.NoContent(http.StatusNoContent)
        }
    }
}

用法

這裏增加了兩個變量,AllowSubDomain,允許二級域名,MainDomain根域名
CORSWithConfig(CORSConfig{AllowCredentials:true,AllowSubDomain:true,MainDomain:"main.com"})

帶cookie跨域問題的思路以及echo的解決方案