Negroni CORS中间件仅用于OPTIONS请求

时间:2018-09-23 21:13:44

标签: authentication go cors mux negroni

我正在使用Gox作为路由器并使用Negroni来管理API的中间件,从而在Golang中构建了一个Web应用。

我有以下中间件:

  • 身份验证中间件,它检查客户端是否具有有效的会话,如果没有,则返回401
  • 允许基于https://github.com/rs/cors/编辑版本的CORS请求的中间件

我想适当地将中间件应用于特定的路由,即对所有请求使用CORS中间件,并仅在受保护的路由上检查auth。

当我尝试使用前端发出API请求时,对我来说实际上发生了什么,那就是CORS中间件似乎只是针对每个API路由的初始OPTIONS请求而被调用,而没有被随后使用。

例如:

  • UI使用包含凭据的JSON对象对/ api / login进行POST
  • 在浏览器控制台中,我们看到初始的preflight OPTIONS请求获得了200 OK,并且我们在corsMiddleware中使用handlePreflight设置的所有标头到目前为止都很好。
  • 然后,所有后续POST请求都没有到达corsMiddleware。我们知道永远不会调用中间件,因为我们永远不会在服务器日志中看到中间件被调用。我们在Chrome中收到错误消息“请求的资源上没有'Access-Control-Allow-Origin'标头”。
  • 奇怪的是实际上调用了handleLogin函数 ,我们可以看到正确的凭据,而且服务器似乎为它们正确存储了会话。

在其他一些路线上,我们也看到了奇怪的事情-例如,在向/ api / entities发出Postman GET请求时,我们能够在未登录时获取实体列表(应受到保护)。身份验证中间件没有被调用或表现不正常。

我对其中的一些概念还很陌生,所以也许我误解了一些东西。任何帮助将不胜感激。

我的代码如下(main.go):

router := mux.NewRouter()
router.HandleFunc("/", ServeUI).Methods("GET")

apiRouter := router.PathPrefix("/api").Subrouter()

authRouter := apiRouter.PathPrefix("/auth").Subrouter()
authRouter.HandleFunc("/login", HandleLogin).Methods("POST")
authRouter.HandleFunc("/logout", HandleLogout).Methods("POST")

entitiesRouter := apiRouter.PathPrefix("/entities").Subrouter()
entitiesRouter.HandleFunc("/", GetEntities).Methods("GET")

commonAPIMiddleware := negroni.New(corsMiddleware.NewCorsMiddleware())

router.PathPrefix("/api/auth").Handler(commonAPIMiddleware.With(
    negroni.Wrap(authRouter),
))

router.PathPrefix("/api/entities").Handler(commonAPIMiddleware.With(
    auth.NewAPIAuthMiddleware(),
    negroni.Wrap(entitiesRouter),
))

n := negroni.New(negronilogrus.NewMiddleware())
n.UseHandler(router)
n.Run(":8009")

corsMiddleware的代码如下:

// CorsMiddleware allows CORS request for api routes
type CorsMiddleware struct {
}

// Negroni compatible interface
func (m *CorsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    log.Info("CORS MIDDLEWARE CALLED")
    if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
        log.Info("ServeHTTP: Preflight request")
        handlePreflight(w, r)
        // Preflight requests are standalone and should stop the chain as some other
        // middleware may not handle OPTIONS requests correctly. One typical example
        // is authentication middleware ; OPTIONS requests won't carry authentication
        // headers (see #1)

        w.WriteHeader(http.StatusOK)
    } else {
        log.Info("ServeHTTP: Actual request")
        handleActualRequest(w, r)
        next(w, r)
    }
}

// handlePreflight handles pre-flight CORS requests
func handlePreflight(w http.ResponseWriter, r *http.Request) {
    headers := w.Header()
    origin := r.Header.Get("Origin")

    if r.Method != http.MethodOptions {
        log.Info("  Preflight aborted: %s!=OPTIONS", r.Method)
        return
    }
    // Always set Vary headers
    // see https://github.com/rs/cors/issues/10,
    //     https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001
    headers.Add("Vary", "Origin")
    headers.Add("Vary", "Access-Control-Request-Method")
    headers.Add("Vary", "Access-Control-Request-Headers")

    if origin == "" {
        log.Info("  Preflight aborted: empty origin")
        return
    }
    headers.Set("Access-Control-Allow-Origin", origin)

    // Spec says: Since the list of methods can be unbounded, simply returning the method indicated
    // by Access-Control-Request-Method (if supported) can be enough
    w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
    headers.Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, X-Requested-With")
    headers.Set("Access-Control-Allow-Credentials", "true")
    headers.Set("Access-Control-Max-Age", strconv.Itoa(1000))
    log.Info("  Preflight response headers: %v", headers)
}

// handleActualRequest handles simple cross-origin requests, actual request or redirects
func handleActualRequest(w http.ResponseWriter, r *http.Request) {
    log.Info("CORS HANDLING ACTUAL REQUEST")
    headers := w.Header()
    origin := r.Header.Get("Origin")

    if r.Method == http.MethodOptions {
        log.Info("  Actual request no headers added: method == %s", r.Method)
        return
    }
    // Always set Vary, see https://github.com/rs/cors/issues/10
    headers.Add("Vary", "Origin")
    if origin == "" {
        log.Info("  Actual request no headers added: missing origin")
        return
    }

    headers.Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, X-Requested-With")
    headers.Set("Access-Control-Allow-Credentials", "true")

    headers.Set("Access-Control-Allow-Origin", origin)

    if true {
        headers.Set("Access-Control-Allow-Credentials", "true")
    }
} 

0 个答案:

没有答案