zoukankan      html  css  js  c++  java
  • 带cookie跨域问题的思路以及echo的解决方案

    问题起因

    前后端分离,前端要访问后端资源,而且需要携带cookie信息,这时碰到了跨域问题。一开始以为设置为允许跨域allow_origins为即可。可是浏览器还是拦截的请求,于是查看跨域规则,原来跨域allow_origins为时,只允许简单的跨域,比如get,post,但是如果携带cookie,则会出现失败。

    思路

    后来查看文档,原来按照跨域请求的规则,当跨域和来源一致时才可以携带cookie。详情见阮一峰的博客

    echo框架中的解决办法

    有了思路就好办了,echo框架中的跨域设置不够详细,做不到设置来源跨域。于是我修改了一下其中的跨域中间件,增加了一子域名的跨域。
    实际上可以修改为,任意来源的跨域,但是这样就不能保证安全了,不过如果是做接口平台倒是可以这么办。
    完整代码为:

    package echo_cors
    
    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
    		AllowAllHost   bool
    	}
    )
    
    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
    				}
    			}
    			if config.AllowAllHost {
    				allowOrigin = c.Scheme()+"://"+req.Host
    			}
    			// 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)
    		}
    	}
    }
    

    用法

    已经放到github
    这里增加了三个变量,AllowSubDomain,允许二级域名,MainDomain根域名,AllowAllHost 允许所有的跨域

    • CORSWithConfig(CORSConfig{AllowCredentials:true,AllowSubDomain:true,MainDomain:"main.com"}) 允许子域名跨域
    • CORSWithConfig(CORSConfig{AllowCredentials:true,AllowAllHost:true})
      对于js,也要做对应修改,axios的修改如下:
    const Axios = axios.create({
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        },
        withCredentials: true
    });
    
    
    Axios.interceptors.request.use(
        config => {
            if (config.method === 'post' || config.method === 'put') {
                if (config.data) {
                    var queryString = Object.keys(config.data)
                        .map(key => {
                            return encodeURIComponent(key) + '=' + encodeURIComponent(config.data[key]);
                        })
                        .join('&');
                    config.data = queryString;
                }
                return config;
            }
            return config;
        },
        error => {
            return Promise.reject(error);
        }
    );
    
  • 相关阅读:
    dubbo-admin 2.0安装部署
    一文多发神器
    springboot整合druid踩坑记录
    thymeleaf中的重定向的绝对路径问题
    路径问题
    sp_executesql介绍和使用
    java.net.UnknownHostException: api.weixin.qq.com解决办法
    调用百度地图示例
    浅析分布式架构
    城乡医保用户角色未分配修改
  • 原文地址:https://www.cnblogs.com/xdao/p/9343442.html
Copyright © 2011-2022 走看看