kubernetes apiserver源码: filter-chain WithCORS

150 阅读2分钟

这个filter给服务增加CORS支持:

  • 使用正则表达式匹配HTTP请求头中的Origin是否被允许
  • 如果允许,添加CORS相关响应头
  • 如果不允许,则无操作。浏览器因为看不到CORS相关响应头而拒绝请求获取响应。
// TODO: use restful.CrossOriginResourceSharing
// See github.com/emicklei/go-restful/blob/master/examples/cors/restful-CORS-filter.go, and
// github.com/emicklei/go-restful/blob/master/examples/basicauth/restful-basic-authentication.go
// Or, for a more detailed implementation use https://github.com/martini-contrib/cors
// or implement CORS at your proxy layer.

// WithCORS is a simple CORS implementation that wraps an http Handler.
// Pass nil for allowedMethods and allowedHeaders to use the defaults. If allowedOriginPatterns
// is empty or nil, no CORS support is installed.
func WithCORS(handler http.Handler, allowedOriginPatterns []string, allowedMethods []string, allowedHeaders []string, exposedHeaders []string, allowCredentials string) http.Handler {
        // 如果无Origin限制,默认拒绝
	if len(allowedOriginPatterns) == 0 {
		return handler
	}
        
        // + 生成允许的orgin的正则表达式
	allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
                // 获取HTTP请求头 Origin
		origin := req.Header.Get("Origin")
		if origin != "" {
			allowed := false
                        // 如果Origin配置其中一个正则,则allowed=true; 
			for _, re := range allowedOriginPatternsREs {
				if allowed = re.MatchString(origin); allowed {
					break
				}
			}
                        // 添加相关CORS响应头,请求被允许; 否则不添加,请求会被浏览器拒绝
			if allowed {
				w.Header().Set("Access-Control-Allow-Origin", origin)
				// Set defaults for methods and headers if nothing was passed
				if allowedMethods == nil {
					allowedMethods = []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"}
				}
				if allowedHeaders == nil {
					allowedHeaders = []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-Requested-With", "If-Modified-Since"}
				}
				if exposedHeaders == nil {
					exposedHeaders = []string{"Date"}
				}
				w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ", "))
				w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowedHeaders, ", "))
				w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposedHeaders, ", "))
				w.Header().Set("Access-Control-Allow-Credentials", allowCredentials)

				// Stop here if its a preflight OPTIONS request
				if req.Method == "OPTIONS" {
					w.WriteHeader(http.StatusNoContent)
					return
				}
			}
		}
		// Dispatch to the next handler
		handler.ServeHTTP(w, req)
	})
}
  • allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)

根据string数组生成正则表达式regexp.Regexp数组

func allowedOriginRegexps(allowedOrigins []string) []*regexp.Regexp {
        // + 编译string => regexp.Regexp
	res, err := compileRegexps(allowedOrigins)
	if err != nil {
		klog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(allowedOrigins, ","), err)
	}
	return res
}

  • res, err := compileRegexps(allowedOrigins)
// Takes a list of strings and compiles them into a list of regular expressions
func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
	regexps := []*regexp.Regexp{}
	for _, regexpStr := range regexpStrings {
		r, err := regexp.Compile(regexpStr)
		if err != nil {
			return []*regexp.Regexp{}, err
		}
		regexps = append(regexps, r)
	}
	return regexps, nil
}

相关API

正则表达式

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile(`(gopher){2}`)
	fmt.Println(re.MatchString("gopher"))
	fmt.Println(re.MatchString("gophergopher"))
	fmt.Println(re.MatchString("gophergophergopher"))

	//Output:
	//
	//false
	//true
	//true
}

自定义HTTP响应头