接下来,我们将把登录的过程也要改造成和数据库验证的模式。首先为了分隔,我们把 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)))
现在我们的注册然后登录登出就基本上可以了。
领取专属 10元无门槛券
私享最新 技术干货