使用 Clojure 建立个人网站(八)

接下来,我们将把登录的过程也要改造成和数据库验证的模式。首先为了分隔,我们把 login 相关的逻辑放到 auth.clj 中,首先删除 core.clj 中的处理函数和路由:

;; 这些要在 core.clj 中删除

(defnlogin-page[request]

(parser/render-file"login.html"request))

(defnhandle-login[{:keys[params]:asrequest}]

(let[email(:emailparams)

password(:passwordparams)]

(cond

(not(auth-validate/validate-emailemail))(res/response{:status400:errors"Email不合法"})

(not(auth-validate/validate-passowordpassword))(res/response{:status400:errors"密码不合法"})

(and(=email"jiesoul@gmail.com")

(=password"12345678"))

(do

(assoc-inrequest[:session:identity]email)

(res/response{:status:ok}))

:else(res/response{:status400:errors"用户名密码不对"}))))

(defnhandle-logout[request]

(do

(assocrequest:session{})

(redirect"/")))

(GET"/login"request(login-pagerequest))

(POST"/login"req(handle-loginreq))

(GET"/logout"request(handle-logoutrequest))

接下来我们开始新 登录和登出逻辑,和注册一样,首先在 auth-validate.cljc 中加入 login-errors 函数

(defnlogin-errors[params]

(first

(b/validate

params

:email[[v/required:message"email 不能为空"]

[v/email:message"email 不合法"]]

:password[[v/required:message"密码不能为空"]

[v/min-count7:message"密码最少8位"]])))

然后回到 auth.clj 中加入相应的路由和处理:

(defnlogin![{:keys[session]}user]

(if(login-errorsuser)

(resp/precondition-failed{:result:error})

(try

(let[db-user(db/select-user(:emailuser))]

(if(or

(nil?db-user)

(not=(:passworddb-user)(hashers/encrypt(:passworduser))))

(resp/precondition-failed

{:result:error

:message"email或密码错误,登录失败"})

(do

(assocuser:last-time(t/now))

(->{:result:ok}

(resp/ok)

(assoc:session(assocsession:identity(:emailuser)))))))

(catchExceptione

(do

(log/errore)

(resp/internal-server-error

{:result:error

:message"发生内部错误,请联系管理员"}))))))

(defnlogout![request]

(do

(assocrequest:session{})

(resp/file-response"/")))

(defauth-routes

(routes

(GET"/register"req(parser/render-file"register.html"req))

(POST"/register"req(register!req(:paramsreq)))

(GET"/login"request(parser/render-file"login.html"request))

(POST"/login"req(login!req(:paramsreq)))

(GET"/logout"request(logout!request))))

修改 login.cljs 中的代码

(nssoul-talk.login

(:require[domina:asdom]

[domina.events:asev]

[reagent.core:asreagent:refer[atom]]

[soul-talk.auth-validate:asvalidate]

[ajax.core:asajax]

[soul-talk.auth-validate:refer[login-errors]]

[taoensso.timbre:aslog]))

(defnlogin![login-dataerrors]

(reset!errors(login-errors@login-data))

(if-not@errors

(ajax/POST"/login"

{:format:json

:headers{"Accept""application/transit+json"}

:params@login-data

:handler#(set!(..js/window-location-href)"/")

:error-handler#(let[msg(get-in%[:response"message"])]

(log/errormsg)

(js/alertmsg))})

(let[error(vals@errors)]

(log/errorerror)

(js/alerterror))))

(defnlogin-component[]

(let[login-data(atom{})

errors(atom{})]

;;...

[:input#submit.btn.btn-primary.btn-lg.btn-block

{:type:submit

:value"登录"

:on-click#(login!login-dataerrors)}]

[:input#submit.btn.btn-primary.btn-lg.btn-block

{:type:button

:value"注册"

:on-click#(set!(..js/window-location-href)"/register")}]

[:p.mt-5.mb-3.text-muted"© @2018"]]])))

现在我们操作就可以连起来了,但是在登录成功转到首页时,并没有显示登录成功的状态,下面我们来解决这个问题。我们需要在 core.clj 加入中间件:

(nssoul-talk.core

(:require[ring.adapter.jetty:asjetty]

;;...

[ring.middleware.session:refer[wrap-session]]))

(defapp

(->(routesauth-routesapp-routes)

;;...

(wrap-session);;加入这个

(wrap-defaults(assoc-inapi-defaults[:security:anti-forgery]false))))

然后修改 core.cljs 的代码:

(defnlog-component?[name]

(fn[]

[:div

[:span.navbar-text(str"欢迎你 "name)]

[:a.btn.btn-sm.btn-outline-secondary{:href"/logout"}"退出"]]))

(defnblog-header-component[]

(fn[]

[:div.blog-header.py-3

[:div.row.flex-nowrap.justify-content-between.align-items-center

[:div.col-4.pt-1

[:a.text-muted{:href"#"}"订阅"]]

[:div.col-4.text-center

[:a.blog-header-logo.text-dark{:href"/"}"Soul Talk"]]

[:div.col-4.d-flex.justify-content-end.align-items-center

(if-not(=js/identity"")

[log-component?js/identity]

[:a.btn.btn-sm.btn-outline-secondary{:href"/login"}"登录"])]]]))

还需要在 index.html 里加入这个 identity 变量

varidentity="{{ session.identity }}";

soul_talk.core.init();

这样在登录后我们就可以在首页看到欢迎你的提示,按钮也变成了退出。

(nssoul-talk.components.common)

(defninput[typeidplaceholderfields]

[:input.form-control.input-lg

{:typetype

:placeholderplaceholder

:value(id@fields)

:on-change#(swap!fieldsassocid(->%.-target.-value))}])

(defnform-input[typelabelidplaceholderfieldsoptional?]

[:div.form-group

[:labellabel]

(ifoptional?

[inputtypeidplaceholderfields]

[:div.input-group

[inputtypeidplaceholderfields]

[:span.input-group-addon"*"]])])

;;

(defntext-input[labelidplaceholderfields&[optional?]]

(form-input:textlabelidplaceholderfieldsoptional?))

(defnpassword-input[labelidplaceholderfields&[optional?]]

(form-input:passwordlabelidplaceholderfieldsoptional?))

然后我在 register.cljs 中引入 并且修改 register-component 函数,用上面的公用组件代替,这里我对一些字的显示也做了修改,请自行判断要不要改。

(nssoul-talk.register

(:require[domina:asdom]

[reagent.core:asreagent:refer[atom]]

[soul-talk.auth-validate:asvalidate]

[ajax.core:asajax]

[reagent.session:assession]

[taoensso.timbre:aslog]

[soul-talk.components.common:asc]))

(defnregister-component[]

(let[reg-data(atom{})

error(atomnil)]

(fn[]

[:div.container

[:div#loginForm.form-signin

[:h1.h3.mb-3.font-weight-normal.text-center"Soul Talk"]

[:div

[:div.well.well-sm"* 为必填"]

[c/text-input"Email":email"enter a email, EX: example@xx.com"reg-data]

[c/password-input"密码":password"输入密码最少8位"reg-data]

[c/password-input"确认密码":pass-confirm"确认密码和上面一样"reg-data]

(when-let[error(:server-error@error)]

[:div.alert.alert-dangererror])]

[:div

[:input.btn.btn-primary.btn-block

{:type:submit

:value"注册"

:on-click#(register!reg-dataerror)}]

[:input.btn.btn-primary.btn-block

{:type:submit

:value"登录"

:on-click#(set!(..js/window-location-href)"/login")}]]

[:p.mt-5.mb-3.text-muted"© @2018"]]])))

同样的现在对 login.cljs 做同样的操作:

(nssoul-talk.login

(:require[domina:asdom]

[reagent.core:asreagent:refer[atom]]

[ajax.core:asajax]

[soul-talk.auth-validate:refer[login-errors]]

[taoensso.timbre:aslog]

[soul-talk.components.common:asc]))

(defnlogin-component[]

(let[login-data(atom{})

errors(atom{})]

(fn[]

[:div.container

[:div#loginForm.form-signin

[:h1.h3.mb-3.font-weight-normal.text-center"Please sign in"]

[c/text-input"Email":email"Email Address"login-data]

[c/password-input"密码":password"输入密码"login-data]

[:input#submit.btn.btn-primary.btn-lg.btn-block

{:type:submit

:value"登录"

:on-click#(login!login-dataerrors)}]

[:input#submit.btn.btn-primary.btn-lg.btn-block

{:type:button

:value"注册"

:on-click#(set!(..js/window-location-href)"/register")}]

[:p.mt-5.mb-3.text-muted"© @2018"]]])))

同时,上篇的 logout 函数有一个问题,并不没有删除 session,需要修改一下:

(nssoul-talk.routes.auth

(:require[soul-talk.models.db:asdb]

[ring.util.http-response:asresp]

[buddy.hashers:ashashers]

[taoensso.timbre:aslog]

[soul-talk.auth-validate:refer[reg-errorslogin-errors]]

[compojure.core:refer[routesPOSTGET]]

[selmer.parser:asparser]

[java-time.local:asl]))

(defnlogout![request]

(->"/"

resp/found

(assoc:sessionnil)))

现在我们的注册然后登录登出就基本上可以了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180907G20P6600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券