#nginx #https #clojure #routes #ring
#nginx #https #clojure #маршруты #кольцо
Вопрос:
Я разрабатываю веб-API Clojure, используя Ring и Compojure. API должен иметь возможность принимать запросы HTTP и HTTPS на основе указанного маршрута.
Например:
Рассмотрим следующие маршруты приложений:
(defroutes app-routes
(POST "/route-one" {request :request} (processRequet request))
(POST "/route-two" {request :request} (processRequet request)))
Я хочу, чтобы маршрут-один принимал только HTTP-запросы, а маршрут-два принимал только HTTPS-запросы.
Возможно ли это?
Я попытался запустить jetty со следующими настройками:
(jetty/run-jetty #'app {:join? false :ssl? true :ssl-port 8443 :keystore "./resources/keystore.jks" :key-password "12345678"})
Это позволило API принимать запросы HTTPS, но не блокировало HTTP-запросы к тем же маршрутам.
Я также безуспешно пытался отключить протокол HTTP:
(jetty/run-jetty #'app {:port 5000 :join? false :ssl? true :ssl-port 8443 :keystore "./resources/keystore.jks" :key-password "12345678" :http? false})
Из того, что я прочитал в Интернете, стандартная процедура для кольцевых HTTPS-запросов использует nginx в качестве обратного прокси для управления всеми HTTPS-запросами.
Но я не нашел никакой реализации в Интернете.
Есть идеи?
Комментарии:
1. Вы смотрели на pedestal.io пока? У него есть конфигурация для обработки нескольких «серверов», которая, я думаю, подходит к вашей ситуации.
Ответ №1:
Один из простых способов добиться этого — проверить схему входящего запроса внутри вашего маршрута, например:
(defroutes app-routes
(POST "/route-one" request
(if (= (:scheme req) :http)
(process-requet request)
{:status 403 :body "https not supported"}))
(POST "/route-two" request
(if (= (:scheme req) :https)
(process-requet request)
{:status 403 :body "http not supported"})))
Вы, конечно, можете извлечь эту проверку схемы в отдельную функцию или макрос, поэтому, даже если у вас много маршрутов, это может быть жизнеспособным вариантом без лишнего шума в вашем коде.
Если у вас много маршрутов, и вы не хотите добавлять дополнительный вызов или проверку к каждому, то другой способ добиться этого — добавить некоторое промежуточное программное обеспечение в ваше приложение. Промежуточное программное обеспечение может проверять :scheme
, включенные в запрос, и отклонять запросы, которые не соответствуют. Например:
(defn wrap-https-only [handler]
(fn [req]
(if (= (:scheme req) :https)
(handler req)
{:status 403 :body "http not supported"})))
(defn wrap-http-only [handler]
(fn [req]
(if (= (:scheme req) :http)
(handler req)
{:status 403 :body "https not supported"})))
Сложность заключается в том, что вы хотите выборочно применять это промежуточное программное обеспечение к некоторым маршрутам, а не к другим, и Compojure не предлагает простого способа сделать это. Если у вас есть какой-то общий путь во всех ваших http-маршрутах и все ваши https-маршруты (для их группировки), вы можете использовать шаблон, подобный этому:
(def http-routes
(-> (routes (POST "/http-only/route-one" request
(process-requet request)))
wrap-http-only))
(def https-routes
(-> (routes (POST "/http-only/route-two" request
(process-requet request)))
wrap-https-only))
(defroutes app-routes
(ANY "/http-only/*" [] http-routes)
(ANY "/https-only/*" [] https-routes))
Здесь вы можете видеть, что промежуточное программное обеспечение применяется к каждому подмножеству маршрутов, и поэтому оно будет выполнено только в том случае, если первая часть пути совпадает. Теперь вы можете добавить столько маршрутов, сколько захотите, а промежуточное программное обеспечение позаботится о проверке схемы для всех из них. Обратите внимание, что этот подход требует, чтобы вы могли каким-либо образом идентифицировать подмножества маршрутов в начальных маршрутах «app-routes», в данном случае я идентифицировал их по пути.
При проверке схемы следует отметить одну вещь: имейте в виду, что это не сработает, если вы решите перенести SSL на балансировщик нагрузки. В этом случае ваше приложение всегда будет получать HTTP (не HTTPS) запросы. Обычно вы можете добиться того же, просто проверив X-Forwarded-Proto
заголовок вместо :scheme
значения.