带cookie跨域问题的思路以及echo的解决方案

Posted xdao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带cookie跨域问题的思路以及echo的解决方案相关的知识,希望对你有一定的参考价值。

问题起因

前后端分离,前端要访问后端资源,而且需要携带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的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

带cookie的异步ajax跨域请求问题

jQuery+ASP.NET MVC基于CORS实现带cookie的跨域ajax请求

跨域请求如何携带cookie?不小心都拿了Offer

Egg 中通过 Egg-cors 配置服务器端允许跨域以及 Cookie 允许跨域

Egg 中通过 Egg-cors 配置服务器端允许跨域以及 Cookie 允许跨域

Koa 中通过 Koa2-cors 配置服务器端允许跨域以及 Cookie 允许跨域