溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄

發(fā)布時(shí)間:2022-04-28 10:52:36 來(lái)源:億速云 閱讀:133 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄”吧!

系統(tǒng)環(huán)境:MacOS 10.13.3

關(guān)于npm安裝速度慢或不成功

使用淘寶鏡像安裝

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

然后所有的 npm install 改為 cnpm install

項(xiàng)目啟動(dòng)

1.初始化項(xiàng)目

$ npm install

2.啟動(dòng)項(xiàng)目

$ npm run dev

3.啟動(dòng)MongoDB

$ mongod --dbpath XXX

xxx是項(xiàng)目里 data 文件夾(也可以另行新建,數(shù)據(jù)庫(kù)用于存放數(shù)據(jù))的路徑,也可直接拖入終端。

4.啟動(dòng)服務(wù)端

$ node server.js

前端UI

vue的首選UI庫(kù)我是選擇了餓了么的Element-UI了,其他諸如 iview 、 vue-strap 好像沒(méi)有ele全面。

安裝Element-UI

$ npm i element-ui -S

引入Element-UI

//在項(xiàng)目里的mian.js里增加下列代碼
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

利用UI里面的選項(xiàng)卡切換做注冊(cè)和登錄界面的切換,以login組件作為整個(gè)登錄系統(tǒng)的主界面,register組件作為獨(dú)立組件切入。Element-UI的組成方式,表單驗(yàn)證等API請(qǐng)查閱官網(wǎng)。

//login組件
<template>
 <div class="login">
  <el-tabs v-model="activeName" @tab-click="handleClick">
   <el-tab-pane label="登錄" name="first">
    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
     <el-form-item label="名稱(chēng)" prop="name">
      <el-input v-model="ruleForm.name"></el-input>
     </el-form-item>
     <el-form-item label="密碼" prop="pass">
      <el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
     </el-form-item>
     <el-form-item>
      <el-button type="primary" @click="submitForm('ruleForm')">登錄</el-button>
      <el-button @click="resetForm('ruleForm')">重置</el-button>
     </el-form-item>
    </el-form>
   </el-tab-pane>
   <el-tab-pane label="注冊(cè)" name="second">
    <register></register>
   </el-tab-pane>
  </el-tabs>
 </div>
</template>
<script>
import register from '@/components/register'
export default {
 data() {
  var validatePass = (rule, value, callback) => {
   if (value === '') {
    callback(new Error('請(qǐng)輸入密碼'));
   } else {
    if (this.ruleForm.checkPass !== '') {
     this.$refs.ruleForm.validateField('checkPass');
    }
    callback();
   }
  };
  return {
   activeName: 'first',
   ruleForm: {
    name: '',
    pass: '',
    checkPass: '',
   },
   rules: {
    name: [
     { required: true, message: '請(qǐng)輸入您的名稱(chēng)', trigger: 'blur' },
     { min: 2, max: 5, message: '長(zhǎng)度在 2 到 5 個(gè)字符', trigger: 'blur' }
    ],
    pass: [
     { required: true, validator: validatePass, trigger: 'blur' }
    ]
   },

  };
 },
 methods: {
  //選項(xiàng)卡切換
  handleClick(tab, event) {
  },
  //重置表單
  resetForm(formName) {
   this.$refs[formName].resetFields();
  },
  //提交表單
  submitForm(formName) {
   this.$refs[formName].validate((valid) => {
    if (valid) {
     this.$message({
      type: 'success',
      message: '登錄成功'
     });
     this.$router.push('HelloWorld');
    } else {
     console.log('error submit!!');
     return false;
    }
   });
  },
 },
 components: {
  register
 }
}
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
 width: 400px;
 margin: 0 auto;
}
#app {
 font-family: 'Avenir', Helvetica, Arial, sans-serif;
 -webkit-font-smoothing: antialiased;
 -moz-osx-font-smoothing: grayscale;
 text-align: center;
 color: #2c3e50;
 margin-top: 60px;
}
.el-tabs__item {
 text-align: center;
 width: 60px;
}
</style>

接下來(lái)是注冊(cè)組件

//register組件
<template>
 <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
  <el-form-item label="名稱(chēng)" prop="name">
   <el-input v-model="ruleForm.name"></el-input>
  </el-form-item>
  <el-form-item label="密碼" prop="pass">
   <el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
  </el-form-item>
  <el-form-item label="確認(rèn)密碼" prop="checkPass">
   <el-input type="password" v-model="ruleForm.checkPass" auto-complete="off"></el-input>
  </el-form-item>
  <el-form-item>
   <el-button type="primary" @click="submitForm('ruleForm')">注冊(cè)</el-button>
   <el-button @click="resetForm('ruleForm')">重置</el-button>
  </el-form-item>
 </el-form>
</template>
<script>
export default {
 data() {
  var validatePass = (rule, value, callback) => {
   if (value === '') {
    callback(new Error('請(qǐng)輸入密碼'));
   } else {
    if (this.ruleForm.checkPass !== '') {
     this.$refs.ruleForm.validateField('checkPass');
    }
    callback();
   }
  };
  var validatePass2 = (rule, value, callback) => {
   if (value === '') {
    callback(new Error('請(qǐng)?jiān)俅屋斎朊艽a'));
   } else if (value !== this.ruleForm.pass) {
    callback(new Error('兩次輸入密碼不一致!'));
   } else {
    callback();
   }
  };
  return {
   activeName: 'second',
   ruleForm: {
    name: '',
    pass: '',
    checkPass: '',
   },
   rules: {
    name: [
     { required: true, message: '請(qǐng)輸入您的名稱(chēng)', trigger: 'blur' },
     { min: 2, max: 5, message: '長(zhǎng)度在 2 到 5 個(gè)字符', trigger: 'blur' }
    ],
    pass: [
     { required: true, validator: validatePass, trigger: 'blur' }
    ],
    checkPass: [
     { required: true, validator: validatePass2, trigger: 'blur' }
    ],
   }
  };
 },
 methods: {
  submitForm(formName) {
   this.$refs[formName].validate((valid) => {
    if (valid) {
     this.$message({
      type: 'success',
      message: '注冊(cè)成功'
     });
     // this.activeName: 'first',
    } else {
     console.log('error submit!!');
     return false;
    }
   });
  },
  resetForm(formName) {
   this.$refs[formName].resetFields();
  }
 }
}
</script>

vue-router

vue-router 是vue創(chuàng)建單頁(yè)項(xiàng)目的核心,可以通過(guò)組合組件來(lái)組成應(yīng)用程序,我們要做的是將組件(components)映射到路由(routes),然后告訴 vue-router 在哪里渲染它們。

上面的代碼里已有涉及到一些路由切換,我們現(xiàn)在來(lái)完善路由:

安裝

$ cnpm i vue-router

引入

import Router from 'vue-router'
Vue.use(Router)

在src文件夾下面新建 router(文件夾)/index.js

我們引入了三個(gè)組件:

HelloWorld 登錄后的展示頁(yè)

login 登錄主界面

register 注冊(cè)組件

路由守衛(wèi)

利用 router.beforeEach 路由守衛(wèi)設(shè)置需要先登錄的頁(yè)面。通過(guò) requiresAuth 這個(gè)字段來(lái)判斷該路由是否需要登錄權(quán)限,需要權(quán)限的路由就攔截,然后再判斷是否有token(下文會(huì)講到token),有就直接登錄,沒(méi)有就跳到登錄頁(yè)面。

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import login from '@/components/login'
import register from '@/components/register'
Vue.use(Router)

const router = new Router({
 mode: 'history',
 routes: [{
   path: '/',
   name: 'home',
   component: HelloWorld,
   meta: {
    requiresAuth: true
   }
  },
  {
   path: '/HelloWorld',
   name: 'HelloWorld',
   component: HelloWorld,
  },
  {
   path: '/login',
   name: 'login',
   component: login,
  },
  {
   path: '/register',
   name: 'register',
   component: register,
  },
 ]
});

//注冊(cè)全局鉤子用來(lái)攔截導(dǎo)航
router.beforeEach((to, from, next) => {
 //獲取store里面的token
 let token = store.state.token;
 //判斷要去的路由有沒(méi)有requiresAuth
 if (to.meta.requiresAuth) {
  if (token) {
   next();
  } else {
   next({
    path: '/login',
    query: { redirect: to.fullPath } // 將剛剛要去的路由path作為參數(shù),方便登錄成功后直接跳轉(zhuǎn)到該路由
   });
  }
 } else {
  next(); 
 }
});
export default router;

我們可以看到路由守衛(wèi)中token是從store里面獲取的,意味著我們是把token的各種狀態(tài)存放到store里面,并進(jìn)行獲取,更新,刪除等操作,這就需要引入vuex狀態(tài)管理。

vuex

解釋一下為什么一個(gè)簡(jiǎn)單的注冊(cè)登錄單頁(yè)需要用到vuex:項(xiàng)目中我們各個(gè)組件的操作基本都需要獲取到token進(jìn)行驗(yàn)證,如果組件A存儲(chǔ)了一個(gè)token,組件B要獲取這個(gè)token就涉及到了組件通信,這會(huì)非常繁瑣。引入vuex,不再是組件間的通信,而是組件和store的通信,簡(jiǎn)單方便。

安裝

$ cnpm i vuex --S

引入

在main.js引入store,vue實(shí)例中也要加入store

//引入store
import store from './store'

然后在需要使用vuex的組件中引入

//store index.js
import Vuex from 'vuex'
Vue.use(Vuex)

在src文件夾下面新建 store(文件夾)/index.js

//store index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//初始化時(shí)用sessionStore.getItem('token'),這樣子刷新頁(yè)面就無(wú)需重新登錄
const state = {
  token: window.sessionStorage.getItem('token'),
  username: ''
};
const mutations = {
  LOGIN: (state, data) => {
    //更改token的值
    state.token = data;
    window.sessionStorage.setItem('token', data);
  },
  LOGOUT: (state) => {
    //登出的時(shí)候要清除token
    state.token = null;
    window.sessionStorage.removeItem('token');
  },
  USERNAME: (state, data) => {
    //把用戶(hù)名存起來(lái)
    state.username = data;
    window.sessionStorage.setItem('username', data);
  }
};
const actions = {
  UserLogin({ commit }, data){
    commit('LOGIN', data);
  },
  UserLogout({ commit }){
    commit('LOGOUT');
  },
  UserName({ commit }, data){
    commit('USERNAME', data);
  }
};
export default new Vuex.Store({
  state,
  mutations,
  actions
});

可以看到我們通過(guò)actions提交mutation,進(jìn)行token的更改、清除以及用戶(hù)名儲(chǔ)存的操作。

此時(shí)啟動(dòng)項(xiàng)目,可以看到初步的注冊(cè)登錄界面,點(diǎn)擊注冊(cè)或登錄按鈕可以切換到相應(yīng)界面,并有基礎(chǔ)的表單驗(yàn)證,登錄后會(huì)進(jìn)入helloworld頁(yè)面。

vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄 

我們寫(xiě)好了基礎(chǔ)界面,接下來(lái)就是要把表單數(shù)據(jù)發(fā)送到后臺(tái)并進(jìn)行一系列處理。現(xiàn)在還沒(méi)有后端接口沒(méi)關(guān)系,我們先寫(xiě)好前端axios請(qǐng)求。

axios

vue的通訊之前使用 vue-resource ,有很多坑。直到vue2.0來(lái)臨,直接拋棄 vue-resource ,而使用 axios 。

用途:

封裝ajax,用來(lái)發(fā)送請(qǐng)求,異步獲取數(shù)據(jù)。以Promise為基礎(chǔ)的HTTP客戶(hù)端,適用于:瀏覽器和node.js。

具體API中文說(shuō)明: https://www.kancloud.cn/yunye/axios/234845

安裝

$ cnpm i -S axios

引入

import axios from 'axios'

攔截器

在設(shè)置vue-router那部分加入了路由守衛(wèi)攔截需要登錄的路由,但這種方式只是簡(jiǎn)單的前端路由控制,并不能真正阻止用戶(hù)訪問(wèn)需要登錄權(quán)限的路由。當(dāng)token失效了,但token依然保存在本地。這時(shí)候你去訪問(wèn)需要登錄權(quán)限的路由時(shí),實(shí)際上應(yīng)該讓用戶(hù)重新登錄。這時(shí)候就需要攔截器 interceptors + 后端接口返回的http狀態(tài)碼來(lái)判斷。

在src文件夾下面新建axios.js(和App.vue同級(jí))

//axios.js
import axios from 'axios'
import store from './store'
import router from './router'

//創(chuàng)建axios實(shí)例
var instance = axios.create({
 timeout: 5000, //請(qǐng)求超過(guò)5秒即超時(shí)返回錯(cuò)誤
 headers: { 'Content-Type': 'application/json;charset=UTF-8' },
});

//request攔截器
instance.interceptors.request.use(
 config => {
  //判斷是否存在token,如果存在的話,則每個(gè)http header都加上token
  if (store.state.token) {
   config.headers.Authorization = `token ${store.state.token}`;
  }
  return config;
 }
);

//respone攔截器
instance.interceptors.response.use(
 response => {
  return response;
 },
 error => { //默認(rèn)除了2XX之外的都是錯(cuò)誤的,就會(huì)走這里
  if (error.response) {
   switch (error.response.status) {
    case 401:
     router.replace({ //跳轉(zhuǎn)到登錄頁(yè)面
      path: 'login',
      query: { redirect: router.currentRoute.fullPath } // 將跳轉(zhuǎn)的路由path作為參數(shù),登錄成功后跳轉(zhuǎn)到該路由
     });
   }
  }
  return Promise.reject(error.response);
 }
);

export default {
  //用戶(hù)注冊(cè)
  userRegister(data){
    return instance.post('/api/register', data);
  },
  //用戶(hù)登錄
  userLogin(data){
    return instance.post('/api/login', data); 
  },
  //獲取用戶(hù)
  getUser(){
    return instance.get('/api/user');
  },
  //刪除用戶(hù)
  delUser(data){
    return instance.post('/api/delUser', data);
  }
}

代碼最后暴露了四個(gè)請(qǐng)求方法,分別對(duì)應(yīng)注冊(cè)(register)、登錄(login)、獲取(user)、刪除(delUser)用戶(hù),并且都在/api下面,四個(gè)請(qǐng)求接口分別是:

http://localhost:8080/api/login
http://localhost:8080/api/register
http://localhost:8080/api/user
http://localhost:8080/api/delUser
后面我們?cè)倮眠@四個(gè)方法寫(xiě)相對(duì)應(yīng)的后臺(tái)接口。

服務(wù)端 server

注意

文章從這里開(kāi)始進(jìn)入服務(wù)端,由于服務(wù)端需要和數(shù)據(jù)庫(kù)、http安全通訊(jwt)共同搭建,因此請(qǐng)結(jié)合本節(jié)和下面的數(shù)據(jù)庫(kù)、jwt章節(jié)閱讀。

koa2可以使用可以使用async/await語(yǔ)法,免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并使用ctx來(lái)訪問(wèn)Context對(duì)象。

現(xiàn)在我們用koa2寫(xiě)項(xiàng)目的API服務(wù)接口。

安裝

$ cnpm i koa
$ cnpm i koa-router -S   //koa路由中間件
$ cnpm i koa-bodyparser -S //處理post請(qǐng)求,并把koa2上下文的表單數(shù)據(jù)解析到ctx.request.body中

引入

const Koa = require('koa');

在項(xiàng)目根目錄下面新建server.js,作為整個(gè)server端的啟動(dòng)入口。

//server.js
const Koa = require('koa');
const app = new Koa();

//router
const Router = require('koa-router');

//父路由
const router = new Router();

//bodyparser:該中間件用于post請(qǐng)求的數(shù)據(jù)
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());

//引入數(shù)據(jù)庫(kù)操作方法
const UserController = require('./server/controller/user.js');

//checkToken作為中間件存在
const checkToken = require('./server/token/checkToken.js');

//登錄
const loginRouter = new Router();
loginRouter.post('/login', UserController.Login);
//注冊(cè)
const registerRouter = new Router();
registerRouter.post('/register', UserController.Reg);

//獲取所有用戶(hù)
const userRouter = new Router();
userRouter.get('/user', checkToken, UserController.GetAllUsers);
//刪除某個(gè)用戶(hù)
const delUserRouter = new Router();
delUserRouter.post('/delUser', checkToken, UserController.DelUser);

//裝載上面四個(gè)子路由
router.use('/api',loginRouter.routes(),loginRouter.allowedMethods());
router.use('/api',registerRouter.routes(),registerRouter.allowedMethods());
router.use('/api',userRouter.routes(),userRouter.allowedMethods());
router.use('/api',delUserRouter.routes(),delUserRouter.allowedMethods());

//加載路由中間件
app.use(router.routes()).use(router.allowedMethods());

app.listen(8888, () => {
  console.log('The server is running at http://localhost:' + 8888);
});

代碼里可以看到,獲取用戶(hù)和刪除用戶(hù)都需要驗(yàn)證token(詳見(jiàn)下文jwt章節(jié)),并且我們把四個(gè)接口掛在到了/api上,和前面axios的請(qǐng)求路徑一致。

接口地址配置

另外由于我們的項(xiàng)目啟動(dòng)端口是8080,koa接口監(jiān)聽(tīng)的端口是8888,于是需要在config/index.js文件里面,在dev配置里加上:

proxyTable: {
  '/api': {
    target: 'http://localhost:8888',
    changeOrigin: true
  }
},
jsonwebtoken(JWT)

JWT能夠在HTTP通信過(guò)程中,幫助我們進(jìn)行身份認(rèn)證。

具體API詳見(jiàn): https://segmentfault.com/a/1190000009494020

Json Web Token是怎么工作的?

1、客戶(hù)端通過(guò)用戶(hù)名和密碼登錄服務(wù)器;

2、服務(wù)端對(duì)客戶(hù)端身份進(jìn)行驗(yàn)證;

3、服務(wù)端對(duì)該用戶(hù)生成Token,返回給客戶(hù)端;

4、客戶(hù)端將Token保存到本地瀏覽器,一般保存到cookie(本文是用sessionStorage,看情況而定)中;

5、客戶(hù)端發(fā)起請(qǐng)求,需要攜帶該Token;

6、服務(wù)端收到請(qǐng)求后,首先驗(yàn)證Token,之后返回?cái)?shù)據(jù)。服務(wù)端不需要保存Token,只需要對(duì)Token中攜帶的信息進(jìn)行驗(yàn)證即可。無(wú)論客戶(hù)端訪問(wèn)后臺(tái)的哪臺(tái)服務(wù)器,只要可以通過(guò)用戶(hù)信息的驗(yàn)證即可。

在server文件夾,下面新建/token(文件夾)里面新增checkToken.js和createToken.js,分別放置檢查和新增token的方法。

安裝

$ cnpm i jsonwebtoken -S
createToken.js

const jwt = require('jsonwebtoken');
module.exports = function(user_id){
  const token = jwt.sign({user_id: user_id}, 'zhangzhongjie', {expiresIn: '60s'
  });
  return token;
};

創(chuàng)建token時(shí),我們把用戶(hù)名作為JWT Payload的一個(gè)屬性,并且把密鑰設(shè)置為‘zhangzhongjie',token過(guò)期時(shí)間設(shè)置為60s。意思是登錄之后,60s內(nèi)刷新頁(yè)面不需要再重新登錄。

checkToken.js

const jwt = require('jsonwebtoken');
//檢查token是否過(guò)期
module.exports = async ( ctx, next ) => {
  //拿到token
  const authorization = ctx.get('Authorization');
  if (authorization === '') {
    ctx.throw(401, 'no token detected in http headerAuthorization');
  }
  const token = authorization.split(' ')[1];
  let tokenContent;
  try {
    tokenContent = await jwt.verify(token, 'zhangzhongjie');//如果token過(guò)期或驗(yàn)證失敗,將拋出錯(cuò)誤
  } catch (err) {
    ctx.throw(401, 'invalid token');
  }
  await next();
};

先拿到token再用jwt.verify進(jìn)行驗(yàn)證,注意此時(shí)密鑰要對(duì)應(yīng)上createToken.js的密鑰‘zhangzhongjie'。如果token為空、過(guò)期、驗(yàn)證失敗都拋出401錯(cuò)誤,要求重新登錄。

數(shù)據(jù)庫(kù) mongodb

MongoDB是一種文檔導(dǎo)向數(shù)據(jù)庫(kù)管理系統(tǒng),旨在為 WEB 應(yīng)用提供可擴(kuò)展的高性能數(shù)據(jù)存儲(chǔ)解決方案。用node鏈接MongoDB非常方便。

安裝

$ cnpm i mongoose -S

MongoDB的連接有好幾種方式,這里我們用connection。connection是mongoose模塊的默認(rèn)引用,返回一個(gè)Connetion對(duì)象。

在server文件夾下新建db.js,作為數(shù)據(jù)庫(kù)連接入口。

//db.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/vue-login');

let db = mongoose.connection;
// 防止Mongoose: mpromise 錯(cuò)誤
mongoose.Promise = global.Promise;

db.on('error', function(){
  console.log('數(shù)據(jù)庫(kù)連接出錯(cuò)!');
});
db.on('open', function(){
  console.log('數(shù)據(jù)庫(kù)連接成功!');
});

//聲明schema
const userSchema = mongoose.Schema({
  username: String,
  password: String,
  token: String,
  create_time: Date
});
//根據(jù)schema生成model
const User = mongoose.model('User', userSchema)
module.exports = User;

除了我們用的 connetion ,還有 connect() 和 createConnection() 連接方式。

Schema定義表的模板,讓這一類(lèi)document在數(shù)據(jù)庫(kù)中有一個(gè)具體的構(gòu)成、存儲(chǔ)模式。但也僅僅是定義了Document是什么樣子的,至于生成document和對(duì)document進(jìn)行各種操作(增刪改查)則是通過(guò)相對(duì)應(yīng)的model來(lái)進(jìn)行的,那我們就需要把userSchema轉(zhuǎn)換成我們可以使用的model,也就是說(shuō)model才是我們可以進(jìn)行操作的handle。

編譯完model我們就得到了一個(gè)名為 User 的model。

注意你在這里定義的schema表,后面寫(xiě)注冊(cè)入庫(kù)時(shí)數(shù)據(jù)的存儲(chǔ)需要對(duì)應(yīng)這個(gè)表。

在server文件夾下新建controller(文件夾)/user.js,存放數(shù)據(jù)庫(kù)的操作方法。

先安裝一些功能插件

$ cnpm i moment -s         //用于生成時(shí)間
$ cnpm i objectid-to-timestamp -s //用于生成時(shí)間
$ cnpm i sha1 -s          //安全哈希算法,用于密碼加密
//user.js
const User = require('../db.js').User;
//下面這兩個(gè)包用來(lái)生成時(shí)間
const moment = require('moment');
const objectIdToTimestamp = require('objectid-to-timestamp');
//用于密碼加密
const sha1 = require('sha1');
//createToken
const createToken = require('../token/createToken.js');

//數(shù)據(jù)庫(kù)的操作
//根據(jù)用戶(hù)名查找用戶(hù)
const findUser = (username) => {
  return new Promise((resolve, reject) => {
    User.findOne({ username }, (err, doc) => {
      if(err){
        reject(err);
      }
      resolve(doc);
    });
  });
};
//找到所有用戶(hù)
const findAllUsers = () => {
  return new Promise((resolve, reject) => {
    User.find({}, (err, doc) => {
      if(err){
        reject(err);
      }
      resolve(doc);
    });
  });
};
//刪除某個(gè)用戶(hù)
const delUser = function(id){
  return new Promise(( resolve, reject) => {
    User.findOneAndRemove({ _id: id }, err => {
      if(err){
        reject(err);
      }
      console.log('刪除用戶(hù)成功');
      resolve();
    });
  });
};
//登錄
const Login = async ( ctx ) => {
  //拿到賬號(hào)和密碼
  let username = ctx.request.body.name;
  let password = sha1(ctx.request.body.pass);//解密
  let doc = await findUser(username);  
  if(!doc){
    console.log('檢查到用戶(hù)名不存在');
    ctx.status = 200;
    ctx.body = {
      info: false
    }
  }else if(doc.password === password){
    console.log('密碼一致!');

     //生成一個(gè)新的token,并存到數(shù)據(jù)庫(kù)
    let token = createToken(username);
    console.log(token);
    doc.token = token;
    await new Promise((resolve, reject) => {
      doc.save((err) => {
        if(err){
          reject(err);
        }
        resolve();
      });
    });
    ctx.status = 200;
    ctx.body = { 
      success: true,
      username,
      token, //登錄成功要?jiǎng)?chuàng)建一個(gè)新的token,應(yīng)該存入數(shù)據(jù)庫(kù)
      create_time: doc.create_time
    };
  }else{
    console.log('密碼錯(cuò)誤!');
    ctx.status = 200;
    ctx.body = {
      success: false
    };
  }
};
//注冊(cè)
const Reg = async ( ctx ) => {
  let user = new User({
    username: ctx.request.body.name,
    password: sha1(ctx.request.body.pass), //加密
    token: createToken(this.username), //創(chuàng)建token并存入數(shù)據(jù)庫(kù)
    create_time: moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss'),//將objectid轉(zhuǎn)換為用戶(hù)創(chuàng)建時(shí)間
  });
  //將objectid轉(zhuǎn)換為用戶(hù)創(chuàng)建時(shí)間(可以不用)
  user.create_time = moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss');

  let doc = await findUser(user.username);
  if(doc){ 
    console.log('用戶(hù)名已經(jīng)存在');
    ctx.status = 200;
    ctx.body = {
      success: false
    };
  }else{
    await new Promise((resolve, reject) => {
      user.save((err) => {
        if(err){
          reject(err);
        }  
        resolve();
      });
    });
    console.log('注冊(cè)成功');
    ctx.status = 200;
    ctx.body = {
      success: true
    }
  }
};
//獲得所有用戶(hù)信息
const GetAllUsers = async( ctx ) => {
  //查詢(xún)所有用戶(hù)信息
  let doc = await findAllUsers();
  ctx.status = 200;
  ctx.body = {
    succsess: '成功',
    result: doc
  };
};

//刪除某個(gè)用戶(hù)
const DelUser = async( ctx ) => {
  //拿到要?jiǎng)h除的用戶(hù)id
  let id = ctx.request.body.id;
  await delUser(id);
  ctx.status = 200;
  ctx.body = {
    success: '刪除成功'
  };
};

module.exports = {
  Login,
  Reg,
  GetAllUsers,
  DelUser
};

上面這些方法構(gòu)成了項(xiàng)目中數(shù)據(jù)庫(kù)操作的核心,我們來(lái)剖析一下。

首先定義了公用的三個(gè)基礎(chǔ)方法:findUser、findAllUsers、delUser。其中findUser需要傳入 username 參數(shù),delUser需要傳入 id 參數(shù)。

注冊(cè)方法

拿到用戶(hù)post提交的表單信息,new之前按數(shù)據(jù)庫(kù)設(shè)計(jì)好的并編譯成model的User,把獲取到的用戶(hù)名,密碼(需要用sha1哈希加密),token(利用之前創(chuàng)建好的createToken方法,并把用戶(hù)名作為jwt的payload參數(shù)),生成時(shí)間存入。

此時(shí)要先搜索數(shù)據(jù)庫(kù)這個(gè)用戶(hù)名是否存在,存在就返回失敗,否則把user存入數(shù)據(jù)庫(kù)并返回成功。

登錄方法

拿到用戶(hù)post的表單信息,用戶(hù)名和密碼(注冊(cè)用了哈希加密,此時(shí)要解密)。從數(shù)據(jù)庫(kù)搜索該用戶(hù)名,判斷用戶(hù)名是否存在,不存在返回錯(cuò)誤,存在的話判斷數(shù)據(jù)庫(kù)里存的密碼和用戶(hù)提交的密碼是否一致,一致的話給這個(gè)用戶(hù)生成一個(gè)新的token,并存入數(shù)據(jù)庫(kù),返回成功。

獲得所有用戶(hù)信息

就是把上面公用findAllUsers方法封裝了一下并把信息放在result里面,讓后面helloworld頁(yè)面可以獲取到這個(gè)數(shù)據(jù)并展示出來(lái)。

刪除某個(gè)用戶(hù)

注意要先拿到需要?jiǎng)h除的用戶(hù)id,作為參數(shù)傳入。

寫(xiě)完這些方法,就可以把前面沒(méi)有完善的注冊(cè)登錄功能完善了。

數(shù)據(jù)庫(kù)可視化

當(dāng)我們注冊(cè)完,數(shù)據(jù)入庫(kù),此時(shí)我們想查看一下剛才注冊(cè)入庫(kù)的數(shù)據(jù),要用到數(shù)據(jù)庫(kù)可視化工具。我是用 MongoBooster ,操作簡(jiǎn)單。

由下圖可以看到示例中注冊(cè)的兩條數(shù)據(jù),包含了id、username、password、token、time。那串長(zhǎng)長(zhǎng)的密碼是由于哈希加密編譯而成。

vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄 

整合完善注冊(cè)組件

在register.vue的表單驗(yàn)證后加上下列代碼

//register.vue
if (valid) {
 axios.userRegister(this.ruleForm)
  .then(({}) => {
   if (data.success) {
    this.$message({
     type: 'success',
     message: '注冊(cè)成功'
    });
   } else {
    this.$message({
     type: 'info',
     message: '用戶(hù)名已經(jīng)存在'
    });
   }
  })
}

完善登錄組件

登錄組件我們之前沒(méi)有任何數(shù)據(jù)提交,現(xiàn)在在驗(yàn)證成功后加入一系列方法完成登錄操作:

引入axios

import axios from '../axios.js'

然后在login.vue的表單驗(yàn)證后加上下列代碼

//login.vue
if (valid) {
 axios.userLogin(this.ruleForm)
  .then(({ data }) => {
   //賬號(hào)不存在
   if (data.info === false) {
    this.$message({
     type: 'info',
     message: '賬號(hào)不存在'
    });
    return;
   }
   //賬號(hào)存在
   if (data.success) {
    this.$message({
     type: 'success',
     message: '登錄成功'
    });
    //拿到返回的token和username,并存到store
    let token = data.token;
    let username = data.username;
    this.$store.dispatch('UserLogin', token);
    this.$store.dispatch('UserName', username);
    //跳到目標(biāo)頁(yè)
    this.$router.push('HelloWorld');
   }
  });
}

將表單數(shù)據(jù)提交到后臺(tái),返回data狀態(tài),進(jìn)行賬號(hào)存在與否的判斷操作。登錄成功需要拿到返回的token和username存到store,跳到目標(biāo)HelloWorld頁(yè)。

完善目標(biāo)頁(yè)組件

注冊(cè)登錄成功后,終于到了實(shí)際的展示頁(yè)了——helloworld!

我們來(lái)完善這個(gè)組件,讓它展示出目前所有的已注冊(cè)用戶(hù)名,并給出刪除按鈕。

//Helloworld.vue
<template>
 <div class="hello">
  <ul>
   <li v-for="(item,index) in users" :key="item._id">
    {{ index + 1 }}.{{ item.username }}
    <el-button @click="del_user(index)">刪除</el-button>
   </li>
  </ul>
  <el-button type="primary" @click="logout()">注銷(xiāo)</el-button>
 </div>
</template>

<script>
import axios from '../axios.js'
export default {
 name: 'HelloWorld',
 data () {
  return {
   users:''
  }
 },
 created(){
  axios.getUser().then((response) => {
   if(response.status === 401){
    //不成功跳轉(zhuǎn)回登錄頁(yè)
    this.$router.push('/login');
    //并且清除掉這個(gè)token
    this.$store.dispatch('UserLogout');
   }else{
    //成功了就把data.result里的數(shù)據(jù)放入users,在頁(yè)面展示
    this.users = response.data.result;
   }
  })
 },
 methods:{
  del_user(index, event){
   let thisID = {
    id:this.users[index]._id
   }
   axios.delUser(thisID)
    .then(response => {
     this.$message({
      type: 'success',
      message: '刪除成功'
     });
     //移除節(jié)點(diǎn)
     this.users.splice(index, 1);
    }).catch((err) => {
     console.log(err);
   });
  },
  logout(){
   //清除token
   this.$store.dispatch('UserLogout');
   if (!this.$store.state.token) {
    this.$router.push('/login')
    this.$message({
     type: 'success',
     message: '注銷(xiāo)成功'
    })
   } else {
    this.$message({
     type: 'info',
     message: '注銷(xiāo)失敗'
    })
   }
  },
 }
}
</script>

<style scoped>
h2, h3 {
 font-weight: normal;
}
ul {
 list-style-type: none;
 padding: 0;
}
li {
 display: inline-block;
 margin: 0 10px;
}
a {
 color: #42b983;
}
.hello {
 font-family: 'Avenir', Helvetica, Arial, sans-serif;
 -webkit-font-smoothing: antialiased;
 -moz-osx-font-smoothing: grayscale;
 text-align: center;
 color: #2c3e50;
 width: 400px;
 margin: 60px auto 0 auto;
}
</style>

輸出頁(yè)面比較簡(jiǎn)單,這里說(shuō)幾個(gè)要點(diǎn):

1.要在實(shí)例創(chuàng)建完成后( created() )立即請(qǐng)求getUser()接口,請(qǐng)求失敗要清楚掉token,請(qǐng)求成功要把返回?cái)?shù)據(jù)放入user以供頁(yè)面渲染。

2. thisID 要寫(xiě)成對(duì)象格式,否則會(huì)報(bào)錯(cuò)

3.注銷(xiāo)時(shí)要清除掉token

到此,相信大家對(duì)“vue2.0+koa2+mongodb怎么實(shí)現(xiàn)注冊(cè)登錄”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI