前面的文章介紹了Vue+Vant+koa2+MongoDB實現用戶註冊,今天咱們來說解一下用戶登錄的實現。前端
主要實現:vue
一、前端經過axios向後端發送username、passwordios
二、後端檢查用戶名是否已註冊、比對password是否正確。web
三、後端生成JWT令牌返回給前端,前端存儲於localStorage中。ajax
其中,圖形驗證碼部分將在下一節中講解。數據庫
效果截圖:json
登錄成功,返回code:200 msg: 登錄成功,並打印出tokenaxios
未註冊用戶返回:後端
密碼錯誤返回:api
JWT簡介:
JWT全稱JSON WEB TOKEN,是目前最流行的跨域認證解決方案,假設用戶登錄成功以後,首先訪問A頁面,而後訪問B頁面,而這兩個頁面的內容都須要根據用戶的身份來獲取,好比說A頁面爲用戶的訂單列表,須要根據user_id來查詢該用戶的全部訂單,B頁面爲用戶的收貨地址列表,又須要根據user_id字段來查詢該用戶的地址列表,而在不一樣頁面之間,這個user_id是如何傳遞的呢,首先咱們能夠利用session,服務端經過cookie將session發送給客戶端,這種方式是將session存儲於服務端,還有一種方式就是服務端經過jwt將user_id、username等信息生成加密的token,客戶端接收以後將其存儲於LocalStroage,這樣當用戶訪問B頁面地址時,配置請求的header訪問頭,將token添加進請求頭,服務端收到token進行解析,便可解析出user_id、username等信息,從而獲取出user_id,從而B頁面就會根據user_id獲取到該用戶的地址列表。
另外,cookie方式不支持跨域,JWT支持跨域認證,例如單點登陸就是依靠JWT 實現。
1、前端部分
於.\vue-mall-mobile\mall\src 新建 Login.vue,仍然採用Vant的輸入框控件:
<template> <div id="login"> <van-cell-group> <van-field v-model="username" label="帳號" /> <van-field v-model="password" type="password" label="密碼" /> </van-cell-group> <van-button square block type="info" native-type="submit" size="normal" @click="Login"> 登錄 </van-button> </div> </template> <script> import { Field } from 'vant'; import { Button } from 'vant'; import { Toast } from 'vant'; import { Cell, CellGroup } from 'vant'; import ajax from '@/api';
export default { components:{ [Field.name]: Field, [Button.name]: Button, [Toast.name]: Toast, [Cell.name]: Cell, [CellGroup.name]: CellGroup, }, data() { return { username:'', password:'', } } } </script>
mothods中添加Login()方法:
async Login() { let { username, password} = this.$data; let res = await ajax.login(username, password); console.log(res) }
在src\api\index.js中添加,經過axios將username、password發送到後端接口:
login(username = '', password = '') { return axios.post('localhost:3000/users/loginUser',{username, password}) }
2、後端部分:
在後端routes\users.js中添加響應路由:
/** * 用戶登錄 */ router.post('/loginUser', async function (ctx) { let {username, password} = ctx.request.body; if(!username || !password) return ctx.body = {code: 4020,msg: '請填寫完整的註冊信息'}; let args = {username, password}; const userData = await userService.accountLogin(args); ctx.body = (userData.code === 200) ? {code: 200, msg: '登錄成功', token: jwt._createToken(userData)} : userData })
在service\userService中添加處理登錄方法 accountLgoin({username, password}):
async accountLogin({username, password}) { const userDoc = await UserModel.findOne({username}); if(!userDoc) return {code: 0, msg: '該用戶還沒有註冊'}; let result = await userDoc.comparePassword(password, userDoc.password); // 進行密碼比對是否一致 return !result ? { code: -2, msg: '密碼不正確' } : { code: 200, _id: userDoc._id, userName: userDoc.userName, gender: userDoc.gender, avatar: userDoc.avatar, mobilePhone: userDoc.mobilePhone, email: userDoc.email, year: userDoc.year, month: userDoc.month, day: userDoc.day }; }
這裏的userDoc.comparePassword()方法,是咱們在定義UserModel是添加的方法,bcryptjs的bcrypt.compare()方法來實現對比咱們在輸入框中輸入的祕密和保存在數據庫裏的hash處理後的密碼。
生成JWT令牌,\utils\jwt.js中的_createToken()方法:
const jwt = require('jsonwebtoken'); /** * 建立 Token */ const _createToken = (userInfo) => { // JWT 格式 token | 有效時間 1 小時 return jwt.sign({ userInfo }, secret, { expiresIn: '1h' }); };
這裏的 {userInfo} 存儲了用戶的id、username、性別等信息,以token令牌的方式存儲於客戶端,也就是前端,等到客戶端發送請求時,在header請求頭中,加入該token,後端經過jwt.virefy()將token中存儲的信息解析出來,解析方法以下,從而使後端代碼能夠經過解析出來的信息認定用戶的身份。
const _verify = (token) => { return jwt.verify(token, secret, (error, decoded) => { console.log(decoded); }); };
3、前端儲存token
方法一:能夠直接在axios返回時,用localStorage.setItem()將token寫入,
localStorage.setItem(USER_TOKEN, JSON.stringify(userToken));
這樣,咱們下次想要訪問該token時,直接:
JSON.parse(localStorage.getItem(USER_TOKEN))
不過咱們應該熟悉Vuex狀態管理,爲後續開發中大型應用作準備,方法二:
在axios返回時,調用this.$store.commit()
(res.token)&&(this.$store.commit('setUserToken', res.token));
setUserToken位於.\src\store\index.js:
export default new Vuex.Store({ state: { token: '', }, mutations: { setUserToken(state, token) { alert(token); state.token = token; } }, }
到這裏,完整的登錄功能就實現啦。下一節咱們會實現圖片驗證碼。
若是文章中有錯誤之處,歡迎你們交流指正。