我正在使用 Ring 和 Compojure 开发 Clojure 网络 API。 API 需要能够接受基于指定路由的 HTTP 和 HTTPS 请求。
例如:
考虑以下应用程序路由:
(defroutes app-routes
(POST "/route-one" {request :request} (processRequet request))
(POST "/route-two" {request :request} (processRequet request)))
我希望route-one 只接受 HTTP 请求,route-two 只接受 HTTPS 请求。
这可能吗?
我尝试使用以下设置运行 jetty:
(jetty/run-jetty #'app {:join? false :ssl? true :ssl-port 8443 :keystore "./resources/keystore.jks" :key-password "12345678"})
这使 API 能够接受 HTTPS 请求,但不会阻止对相同路由的 HTTP 请求。
我也试过禁用 HTTP 协议(protocol),但没有成功:
(jetty/run-jetty #'app {:port 5000 :join? false :ssl? true :ssl-port 8443 :keystore "./resources/keystore.jks" :key-password "12345678" :http? false})
根据我在网上看到的,Ring HTTPS 请求的标准流程是使用nginx 作为反向代理来管理所有 HTTPS 请求。
但是我还没有在网上找到任何实现。
有什么想法吗?
最佳答案
实现此目的的一种简单方法是检查路由中传入请求的方案,例如:
(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 路由(对它们进行分组)中都有一些公共(public)路径,那么您可以使用这样的模式:
(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
header 而不是 :scheme
值来实现相同的目的。
关于nginx - 使用 Clojure 和 Ring 的 HTTPS 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40136412/