使用Clojure和Ring

时间:2016-10-19 15:59:41

标签: nginx https clojure routes ring

我正在使用响铃 Compojure 开发Clojure网络API。 API需要能够根据指定的路由接受HTTP和HTTPS请求。

例如:

请考虑以下 app-routes

(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 个答案:

答案 0 :(得分:3)

实现此目的的一种简单方法是检查路由中传入请求的方案,例如:

(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值即可实现相同目标。