在Clojure Web应用程序中使用JSON请求正文而不是请求参数进行朋友身份验证

时间:2013-12-01 13:23:51

标签: authentication clojure compojure

我正在使用Friend在Compojure Web应用程序中构建身份验证。

我为朋友定义了一个定制的身份验证工作流程:

(defn authentication-workflow []
  (routes
    (GET "/logout" req
      (friend/logout* {:status 200}))
    (POST "/login" {{:keys [username password]} :params}
      (if-let [user-record (authenticate-user username password)]
        (workflows/make-auth user-record {:cemerick.friend/workflow :authorisation-workflow})
        {:status 401}))))

认证部分被考虑在内:

(defn authenticate-user [username password]
  (if-let [user-record (get-user-for-username username)]
    (if (creds/bcrypt-verify password (:password user-record))
      (dissoc user-record :password))))

这有效,但......

我正在使用AngularJS并且必须发布请求参数导致一些丑陋的Angular代码(在StackOverflow答案的其他地方被抄袭):

$http({
    method: 'POST',
    url: '/login',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    transformRequest: function(obj) {
        var str = [];
        for (var p in obj)
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            return str.join("&");
        },
        data: {
            username: username,
            password: password
        }
    });

我宁愿做更简单的调用而只是通过请求体发布一个JSON对象:

$http.post('/login', {username: username, password: password})

我试图在身份验证处理程序中使用“:body”而不是“:params”,但是:body的值似乎既不是JSON也不是Clojure,所以我不知道如何使用它:

{username me@myapp.com, password password}

我已经为我的REST API处理程序正确地运行了JSON请求/响应映射工作流,并且我已经检查过请求标头(例如ContentType)对于JSON是否正确。

这可以通过Compojure / Friend完成,如果是这样的话?

2 个答案:

答案 0 :(得分:3)

这是一些工作代码和解释......

首先使用请求正文的朋友工作流程:

(defn authentication-workflow []
  (routes
    (GET "/logout" req
      (friend/logout* {:status 200}))
    (POST "/login" {body :body}
      (if-let [user-record (authenticate-user body)]
        (workflows/make-auth user-record {:cemerick.friend/workflow :authorisation-workflow})
        {:status 401}))))

其次,验证功能:

(defn authenticate-user [{username "username" password "password"}]
  (if-let [user-record (get-user-for-username username)]
    (if (creds/bcrypt-verify password (:password user-record))
      (dissoc user-record :password))))

第三,声明了中间件的Compojure应用程序:

(def app
  (-> (handler/site
        (friend/authenticate app-routes
          {:workflows [(authentication-workflow)]}))
      (params/wrap-keyword-params)
      (json/wrap-json-body)
      (json/wrap-json-response {:pretty true})))

最后一个AngularJS代码片段发布凭证(用户名和密码来自AngularJS模型):

$http.post('/login', {username: username, password: password});

所以会发生什么......

Angular javascript代码将JSON发布到Web应用程序登录URL。 “Content-Type”标头自动设置为“application / json”,请求主体自动编码为JSON,例如:

{"username":"batman@batcave.org","password":"tumblerrocks"}

在服务器上,中间件将JSON解析为Clojure映射,并通过“:body”关键字将其呈现给处理程序:

{username batman@batcave.org, password tumblerrocks}

然后,请求将路由到自定义Google朋友身份验证工作流程。

最后,提交的值从Clojure映射中提取并用于验证用户。

答案 1 :(得分:1)

我怀疑你的包装器以错误的顺序应用。检查朋友包装之前(之外)是否应用了ring.middleware.json/wrap-json-body

e.g。

(def my-handler (wrap-json-body (cemerick.friend/authenticate ...)))

否则,快速修复可能只是将整个应用程序包装在ring.middleware.json/wrap-json-params