Промежуточное ПО Negroni CORS вызывается только по запросу OPTIONS - PullRequest
0 голосов
/ 24 сентября 2018

Я создаю веб-приложение на Голанге, используя mux в качестве маршрутизатора и Negroni для управления промежуточным ПО для API.

У меня есть следующее промежуточное ПО:

  • Промежуточное ПО аутентификации, котороепроверяет, что клиент имеет действительный сеанс, и возвращает 401, если не
  • Промежуточное программное обеспечение, чтобы разрешить запросы CORS, основанные на отредактированной версии https://github.com/rs/cors/

Я хочу применить промежуточное ПОв зависимости от конкретных маршрутов, т. е. с использованием промежуточного программного обеспечения CORS для всех запросов и проверки аутентификации только на защищенных маршрутах.

Что действительно происходит со мной, когда я пытаюсь выполнять запросы API с использованием внешнего интерфейса, так это то, что промежуточное программное обеспечение CORSКажется, он вызывается только для первоначального запроса OPTIONS для каждого маршрута API и впоследствии не используется.

Например:

  • Пользовательский интерфейс выполняет POST для / api / login с JSONобъект, содержащий учетные данные
  • В консоли обозревателя мы видим, что первоначальный запрос OPTIONS перед полетом получает 200 OK со всемиЗаголовки, которые мы устанавливаем с помощью handlePreflight в corsMiddleware, пока что хороши.
  • Тогда ни один из последующих запросов POST не попал в corsMiddleware.Мы знаем, что промежуточное программное обеспечение никогда не вызывается, поскольку мы никогда не видим его вызываемым в журнале сервера.Мы получаем сообщение об ошибке «Нет заголовка« Access-Control-Allow-Origin »на запрашиваемом ресурсе» в Chrome.
  • Странно то, что функция handleLogin действительно вызывается ,мы можем видеть правильные учетные данные, и кажется, что сервер правильно хранит сеанс для них.

Мы также видим странные вещи на некоторых других маршрутах - то есть, когда отправляем GET-запросы Postman к / api/ entity мы можем получить список сущностей (которые должны быть защищены), когда не вошли в систему. Это похоже на то, что промежуточное ПО аутентификации не вызывается или не ведет себя должным образом.

Я довольно новичокк некоторым из этих концепций, так что, возможно, я что-то неправильно понял.Буду признателен за любую помощь.

Мой код выглядит следующим образом (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")
    }
} 
...