• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    vue2.0+koa2+mongodb實現注冊登錄

    來源:懂視網 責編:小采 時間:2020-11-27 22:16:34
    文檔

    vue2.0+koa2+mongodb實現注冊登錄

    vue2.0+koa2+mongodb實現注冊登錄:前言 前段時間和公司一個由技術轉產品的同事探討他的職業道路,對我說了一句深以為然的話: 不要把自己禁錮在某一個領域,技術到產品的轉變,首先就是思維上的轉變。你一直做前端,數據的交互你只知道怎么進,卻不知道里面是怎么出的,這就是局限性。 醍
    推薦度:
    導讀vue2.0+koa2+mongodb實現注冊登錄:前言 前段時間和公司一個由技術轉產品的同事探討他的職業道路,對我說了一句深以為然的話: 不要把自己禁錮在某一個領域,技術到產品的轉變,首先就是思維上的轉變。你一直做前端,數據的交互你只知道怎么進,卻不知道里面是怎么出的,這就是局限性。 醍

    前言

    前段時間和公司一個由技術轉產品的同事探討他的職業道路,對我說了一句深以為然的話:

    “不要把自己禁錮在某一個領域,技術到產品的轉變,首先就是思維上的轉變。你一直做前端,數據的交互你只知道怎么進,卻不知道里面是怎么出的,這就是局限性。”

    醍醐灌頂般,剛好學習vue的時候看到有個注冊登錄的項目,索性我也跟著動手做一個vue項目,引入koa和mongodb,實現客戶端(client)提交-服務端(server)接收返回-入數據庫全過程。

    本項目基于vue-cli搭建,利用token方式進行用戶登錄驗證,并實現注冊入庫、讀取用戶、刪除用戶等功能。文章默認讀者有一定的node和vue基礎,基礎部分不贅述。

    系統環境:MacOS 10.13.3

    關于npm安裝速度慢或不成功

    使用淘寶鏡像安裝

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

    然后所有的 npm install 改為 cnpm install

    項目流程圖

    為了讓項目思路和所選技術更加清晰明了,畫了一個圖方便理解。

     

    項目啟動

    0.項目地址

    github: https://github.com/stzhongjie/vue-login

    1.初始化項目

    $ npm install

    2.啟動項目

    $ npm run dev

    3.啟動MongoDB

    $ mongod --dbpath XXX

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

    4.啟動服務端

    $ node server.js

    前端UI

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

    安裝Element-UI

    $ npm i element-ui -S

    引入Element-UI

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

    利用UI里面的選項卡切換做注冊和登錄界面的切換,以login組件作為整個登錄系統的主界面,register組件作為獨立組件切入。Element-UI的組成方式,表單驗證等API請查閱官網。

    //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="名稱" 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="注冊" 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('請輸入密碼'));
     } else {
     if (this.ruleForm.checkPass !== '') {
     this.$refs.ruleForm.validateField('checkPass');
     }
     callback();
     }
     };
     return {
     activeName: 'first',
     ruleForm: {
     name: '',
     pass: '',
     checkPass: '',
     },
     rules: {
     name: [
     { required: true, message: '請輸入您的名稱', trigger: 'blur' },
     { min: 2, max: 5, message: '長度在 2 到 5 個字符', trigger: 'blur' }
     ],
     pass: [
     { required: true, validator: validatePass, trigger: 'blur' }
     ]
     },
    
     };
     },
     methods: {
     //選項卡切換
     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>

    接下來是注冊組件

    //register組件
    <template>
     <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
     <el-form-item label="名稱" 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="確認密碼" 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')">注冊</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('請輸入密碼'));
     } else {
     if (this.ruleForm.checkPass !== '') {
     this.$refs.ruleForm.validateField('checkPass');
     }
     callback();
     }
     };
     var validatePass2 = (rule, value, callback) => {
     if (value === '') {
     callback(new Error('請再次輸入密碼'));
     } else if (value !== this.ruleForm.pass) {
     callback(new Error('兩次輸入密碼不一致!'));
     } else {
     callback();
     }
     };
     return {
     activeName: 'second',
     ruleForm: {
     name: '',
     pass: '',
     checkPass: '',
     },
     rules: {
     name: [
     { required: true, message: '請輸入您的名稱', trigger: 'blur' },
     { min: 2, max: 5, message: '長度在 2 到 5 個字符', 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: '注冊成功'
     });
     // this.activeName: 'first',
     } else {
     console.log('error submit!!');
     return false;
     }
     });
     },
     resetForm(formName) {
     this.$refs[formName].resetFields();
     }
     }
    }
    </script>

    vue-router

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

    上面的代碼里已有涉及到一些路由切換,我們現在來完善路由:

    安裝

    $ cnpm i vue-router

    引入

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

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

    我們引入了三個組件:

    HelloWorld 登錄后的展示頁

    login 登錄主界面

    register 注冊組件

    路由守衛

    利用 router.beforeEach 路由守衛設置需要先登錄的頁面。通過 requiresAuth 這個字段來判斷該路由是否需要登錄權限,需要權限的路由就攔截,然后再判斷是否有token(下文會講到token),有就直接登錄,沒有就跳到登錄頁面。

    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,
     },
     ]
    });
    
    //注冊全局鉤子用來攔截導航
    router.beforeEach((to, from, next) => {
     //獲取store里面的token
     let token = store.state.token;
     //判斷要去的路由有沒有requiresAuth
     if (to.meta.requiresAuth) {
     if (token) {
     next();
     } else {
     next({
     path: '/login',
     query: { redirect: to.fullPath } // 將剛剛要去的路由path作為參數,方便登錄成功后直接跳轉到該路由
     });
     }
     } else {
     next(); 
     }
    });
    export default router;

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

    vuex

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

    安裝

    $ cnpm i vuex --S

    引入

    在main.js引入store,vue實例中也要加入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);
    //初始化時用sessionStore.getItem('token'),這樣子刷新頁面就無需重新登錄
    const state = {
     token: window.sessionStorage.getItem('token'),
     username: ''
    };
    const mutations = {
     LOGIN: (state, data) => {
     //更改token的值
     state.token = data;
     window.sessionStorage.setItem('token', data);
     },
     LOGOUT: (state) => {
     //登出的時候要清除token
     state.token = null;
     window.sessionStorage.removeItem('token');
     },
     USERNAME: (state, data) => {
     //把用戶名存起來
     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
    });
    
    

    可以看到我們通過actions提交mutation,進行token的更改、清除以及用戶名儲存的操作。

    此時啟動項目,可以看到初步的注冊登錄界面,點擊注冊或登錄按鈕可以切換到相應界面,并有基礎的表單驗證,登錄后會進入helloworld頁面。

     

    我們寫好了基礎界面,接下來就是要把表單數據發送到后臺并進行一系列處理。現在還沒有后端接口沒關系,我們先寫好前端axios請求。

    axios

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

    用途:

    封裝ajax,用來發送請求,異步獲取數據。以Promise為基礎的HTTP客戶端,適用于:瀏覽器和node.js。

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

    安裝

    $ cnpm i -S axios

    引入

    import axios from 'axios'

    攔截器

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

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

    //axios.js
    import axios from 'axios'
    import store from './store'
    import router from './router'
    
    //創建axios實例
    var instance = axios.create({
     timeout: 5000, //請求超過5秒即超時返回錯誤
     headers: { 'Content-Type': 'application/json;charset=UTF-8' },
    });
    
    //request攔截器
    instance.interceptors.request.use(
     config => {
     //判斷是否存在token,如果存在的話,則每個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 => { //默認除了2XX之外的都是錯誤的,就會走這里
     if (error.response) {
     switch (error.response.status) {
     case 401:
     router.replace({ //跳轉到登錄頁面
     path: 'login',
     query: { redirect: router.currentRoute.fullPath } // 將跳轉的路由path作為參數,登錄成功后跳轉到該路由
     });
     }
     }
     return Promise.reject(error.response);
     }
    );
    
    export default {
     //用戶注冊
     userRegister(data){
     return instance.post('/api/register', data);
     },
     //用戶登錄
     userLogin(data){
     return instance.post('/api/login', data); 
     },
     //獲取用戶
     getUser(){
     return instance.get('/api/user');
     },
     //刪除用戶
     delUser(data){
     return instance.post('/api/delUser', data);
     }
    }

    代碼最后暴露了四個請求方法,分別對應注冊(register)、登錄(login)、獲取(user)、刪除(delUser)用戶,并且都在/api下面,四個請求接口分別是:

    http://localhost:8080/api/login
    http://localhost:8080/api/register
    http://localhost:8080/api/user
    http://localhost:8080/api/delUser
    后面我們再利用這四個方法寫相對應的后臺接口。

    服務端 server

    注意

    文章從這里開始進入服務端,由于服務端需要和數據庫、http安全通訊(jwt)共同搭建,因此請結合本節和下面的數據庫、jwt章節閱讀。

    koa2可以使用可以使用async/await語法,免除重復繁瑣的回調函數嵌套,并使用ctx來訪問Context對象。

    現在我們用koa2寫項目的API服務接口。

    安裝

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

    引入

    const Koa = require('koa');

    在項目根目錄下面新建server.js,作為整個server端的啟動入口。

    //server.js
    const Koa = require('koa');
    const app = new Koa();
    
    //router
    const Router = require('koa-router');
    
    //父路由
    const router = new Router();
    
    //bodyparser:該中間件用于post請求的數據
    const bodyParser = require('koa-bodyparser');
    app.use(bodyParser());
    
    //引入數據庫操作方法
    const UserController = require('./server/controller/user.js');
    
    //checkToken作為中間件存在
    const checkToken = require('./server/token/checkToken.js');
    
    //登錄
    const loginRouter = new Router();
    loginRouter.post('/login', UserController.Login);
    //注冊
    const registerRouter = new Router();
    registerRouter.post('/register', UserController.Reg);
    
    //獲取所有用戶
    const userRouter = new Router();
    userRouter.get('/user', checkToken, UserController.GetAllUsers);
    //刪除某個用戶
    const delUserRouter = new Router();
    delUserRouter.post('/delUser', checkToken, UserController.DelUser);
    
    //裝載上面四個子路由
    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);
    });

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

    接口地址配置

    另外由于我們的項目啟動端口是8080,koa接口監聽的端口是8888,于是需要在config/index.js文件里面,在dev配置里加上:

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

    JWT能夠在HTTP通信過程中,幫助我們進行身份認證。

    具體API詳見: https://segmentfault.com/a/1190000009494020

    Json Web Token是怎么工作的?

    1、客戶端通過用戶名和密碼登錄服務器;

    2、服務端對客戶端身份進行驗證;

    3、服務端對該用戶生成Token,返回給客戶端;

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

    5、客戶端發起請求,需要攜帶該Token;

    6、服務端收到請求后,首先驗證Token,之后返回數據。服務端不需要保存Token,只需要對Token中攜帶的信息進行驗證即可。無論客戶端訪問后臺的哪臺服務器,只要可以通過用戶信息的驗證即可。

    在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;
    };

    創建token時,我們把用戶名作為JWT Payload的一個屬性,并且把密鑰設置為‘zhangzhongjie',token過期時間設置為60s。意思是登錄之后,60s內刷新頁面不需要再重新登錄。

    checkToken.js

    const jwt = require('jsonwebtoken');
    //檢查token是否過期
    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過期或驗證失敗,將拋出錯誤
     } catch (err) {
     ctx.throw(401, 'invalid token');
     }
     await next();
    };
    
    

    先拿到token再用jwt.verify進行驗證,注意此時密鑰要對應上createToken.js的密鑰‘zhangzhongjie'。如果token為空、過期、驗證失敗都拋出401錯誤,要求重新登錄。

    數據庫 mongodb

    MongoDB是一種文檔導向數據庫管理系統,旨在為 WEB 應用提供可擴展的高性能數據存儲解決方案。用node鏈接MongoDB非常方便。

    安裝

    $ cnpm i mongoose -S

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

    在server文件夾下新建db.js,作為數據庫連接入口。

    //db.js
    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/vue-login');
    
    let db = mongoose.connection;
    // 防止Mongoose: mpromise 錯誤
    mongoose.Promise = global.Promise;
    
    db.on('error', function(){
     console.log('數據庫連接出錯!');
    });
    db.on('open', function(){
     console.log('數據庫連接成功!');
    });
    
    //聲明schema
    const userSchema = mongoose.Schema({
     username: String,
     password: String,
     token: String,
     create_time: Date
    });
    //根據schema生成model
    const User = mongoose.model('User', userSchema)
    module.exports = User;

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

    Schema定義表的模板,讓這一類document在數據庫中有一個具體的構成、存儲模式。但也僅僅是定義了Document是什么樣子的,至于生成document和對document進行各種操作(增刪改查)則是通過相對應的model來進行的,那我們就需要把userSchema轉換成我們可以使用的model,也就是說model才是我們可以進行操作的handle。

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

    注意你在這里定義的schema表,后面寫注冊入庫時數據的存儲需要對應這個表。

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

    先安裝一些功能插件

    $ cnpm i moment -s //用于生成時間
    $ cnpm i objectid-to-timestamp -s //用于生成時間
    $ cnpm i sha1 -s //安全哈希算法,用于密碼加密
    //user.js
    const User = require('../db.js').User;
    //下面這兩個包用來生成時間
    const moment = require('moment');
    const objectIdToTimestamp = require('objectid-to-timestamp');
    //用于密碼加密
    const sha1 = require('sha1');
    //createToken
    const createToken = require('../token/createToken.js');
    
    //數據庫的操作
    //根據用戶名查找用戶
    const findUser = (username) => {
     return new Promise((resolve, reject) => {
     User.findOne({ username }, (err, doc) => {
     if(err){
     reject(err);
     }
     resolve(doc);
     });
     });
    };
    //找到所有用戶
    const findAllUsers = () => {
     return new Promise((resolve, reject) => {
     User.find({}, (err, doc) => {
     if(err){
     reject(err);
     }
     resolve(doc);
     });
     });
    };
    //刪除某個用戶
    const delUser = function(id){
     return new Promise(( resolve, reject) => {
     User.findOneAndRemove({ _id: id }, err => {
     if(err){
     reject(err);
     }
     console.log('刪除用戶成功');
     resolve();
     });
     });
    };
    //登錄
    const Login = async ( ctx ) => {
     //拿到賬號和密碼
     let username = ctx.request.body.name;
     let password = sha1(ctx.request.body.pass);//解密
     let doc = await findUser(username); 
     if(!doc){
     console.log('檢查到用戶名不存在');
     ctx.status = 200;
     ctx.body = {
     info: false
     }
     }else if(doc.password === password){
     console.log('密碼一致!');
    
     //生成一個新的token,并存到數據庫
     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, //登錄成功要創建一個新的token,應該存入數據庫
     create_time: doc.create_time
     };
     }else{
     console.log('密碼錯誤!');
     ctx.status = 200;
     ctx.body = {
     success: false
     };
     }
    };
    //注冊
    const Reg = async ( ctx ) => {
     let user = new User({
     username: ctx.request.body.name,
     password: sha1(ctx.request.body.pass), //加密
     token: createToken(this.username), //創建token并存入數據庫
     create_time: moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss'),//將objectid轉換為用戶創建時間
     });
     //將objectid轉換為用戶創建時間(可以不用)
     user.create_time = moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss');
    
     let doc = await findUser(user.username);
     if(doc){ 
     console.log('用戶名已經存在');
     ctx.status = 200;
     ctx.body = {
     success: false
     };
     }else{
     await new Promise((resolve, reject) => {
     user.save((err) => {
     if(err){
     reject(err);
     } 
     resolve();
     });
     });
     console.log('注冊成功');
     ctx.status = 200;
     ctx.body = {
     success: true
     }
     }
    };
    //獲得所有用戶信息
    const GetAllUsers = async( ctx ) => {
     //查詢所有用戶信息
     let doc = await findAllUsers();
     ctx.status = 200;
     ctx.body = {
     succsess: '成功',
     result: doc
     };
    };
    
    //刪除某個用戶
    const DelUser = async( ctx ) => {
     //拿到要刪除的用戶id
     let id = ctx.request.body.id;
     await delUser(id);
     ctx.status = 200;
     ctx.body = {
     success: '刪除成功'
     };
    };
    
    module.exports = {
     Login,
     Reg,
     GetAllUsers,
     DelUser
    };

    上面這些方法構成了項目中數據庫操作的核心,我們來剖析一下。

    首先定義了公用的三個基礎方法:findUser、findAllUsers、delUser。其中findUser需要傳入 username 參數,delUser需要傳入 id 參數。

    注冊方法

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

    此時要先搜索數據庫這個用戶名是否存在,存在就返回失敗,否則把user存入數據庫并返回成功。

    登錄方法

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

    獲得所有用戶信息

    就是把上面公用findAllUsers方法封裝了一下并把信息放在result里面,讓后面helloworld頁面可以獲取到這個數據并展示出來。

    刪除某個用戶

    注意要先拿到需要刪除的用戶id,作為參數傳入。

    寫完這些方法,就可以把前面沒有完善的注冊登錄功能完善了。

    數據庫可視化

    當我們注冊完,數據入庫,此時我們想查看一下剛才注冊入庫的數據,要用到數據庫可視化工具。我是用 MongoBooster ,操作簡單。

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

     

    整合完善注冊組件

    在register.vue的表單驗證后加上下列代碼

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

    完善登錄組件

    登錄組件我們之前沒有任何數據提交,現在在驗證成功后加入一系列方法完成登錄操作:

    引入axios

    import axios from '../axios.js'

    然后在login.vue的表單驗證后加上下列代碼

    //login.vue
    if (valid) {
     axios.userLogin(this.ruleForm)
     .then(({ data }) => {
     //賬號不存在
     if (data.info === false) {
     this.$message({
     type: 'info',
     message: '賬號不存在'
     });
     return;
     }
     //賬號存在
     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);
     //跳到目標頁
     this.$router.push('HelloWorld');
     }
     });
    }

    將表單數據提交到后臺,返回data狀態,進行賬號存在與否的判斷操作。登錄成功需要拿到返回的token和username存到store,跳到目標HelloWorld頁。

    完善目標頁組件

    注冊登錄成功后,終于到了實際的展示頁了——helloworld!

    我們來完善這個組件,讓它展示出目前所有的已注冊用戶名,并給出刪除按鈕。

    //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()">注銷</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){
     //不成功跳轉回登錄頁
     this.$router.push('/login');
     //并且清除掉這個token
     this.$store.dispatch('UserLogout');
     }else{
     //成功了就把data.result里的數據放入users,在頁面展示
     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: '刪除成功'
     });
     //移除節點
     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: '注銷成功'
     })
     } else {
     this.$message({
     type: 'info',
     message: '注銷失敗'
     })
     }
     },
     }
    }
    </script>
    
    <style scoped>
    h1, h2 {
     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>

    輸出頁面比較簡單,這里說幾個要點:

    1.要在實例創建完成后( created() )立即請求getUser()接口,請求失敗要清楚掉token,請求成功要把返回數據放入user以供頁面渲染。

    2. thisID 要寫成對象格式,否則會報錯

    3.注銷時要清除掉token

    總結

    人的思維轉變確實是最難的。按流程來說,應該是koa先設計出接口,前端再根據這個接口去請求,但我反過來,是先寫好前端請求,再根據這個請求去制定接口。

    當然,也遇到了很多困難:當我搞好了前端展示頁面,axios也寫好了,但在用koa寫接口這里卡了很久,完全沒有概念,就是前言說的“只知道數據怎么進,不知道怎么出”。然后遇到接口500報錯又調試了很久,主要是自己對接口沒有調試概念,最后還是公司的瑯琊大佬幫忙解決,感謝。

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    vue2.0+koa2+mongodb實現注冊登錄

    vue2.0+koa2+mongodb實現注冊登錄:前言 前段時間和公司一個由技術轉產品的同事探討他的職業道路,對我說了一句深以為然的話: 不要把自己禁錮在某一個領域,技術到產品的轉變,首先就是思維上的轉變。你一直做前端,數據的交互你只知道怎么進,卻不知道里面是怎么出的,這就是局限性。 醍
    推薦度:
    標簽: 注冊 VUE 實現
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 蜜国产精品jk白丝AV网站| 国产精品国产三级国产普通话 | 亚洲精品无码乱码成人| 91在线手机精品超级观看| 久久99精品久久久久久hb无码 | 9久热这里只有精品| 99RE6热在线精品视频观看| 四虎成人精品国产永久免费无码| 99久久国产综合精品网成人影院| 国产精品亚洲专区在线观看| 久久国产免费观看精品3| 夜夜精品无码一区二区三区 | 久久精品国产久精国产思思 | 真实国产乱子伦精品一区二区三区| 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲 | 久久国产精品波多野结衣AV| 四虎成人欧美精品在永久在线| 91精品国产91久久综合| 精品国产一区二区三区无码| 亚洲国产精品VA在线观看麻豆 | 国产亚洲婷婷香蕉久久精品| 91精品国产综合久久精品| 国产精品免费无遮挡无码永久视频 | 精品久久久久中文字幕日本| 亚洲AV日韩精品久久久久| 亚洲午夜精品第一区二区8050| 久久国产综合精品五月天| 国内精品久久久久久中文字幕| 国产高清精品在线| 国产精品内射视频免费| 国产精品狼人久久久久影院 | 无码人妻精品一区二区| 中国精品18videosex性中国| 四虎永久在线精品免费一区二区| 免费人妻精品一区二区三区| 日韩精品无码Av一区二区| 亚洲国产精品自在拍在线播放| 亚洲午夜精品一级在线播放放 | 精品人妻码一区二区三区| 久久国产乱子伦免费精品| 国内精品久久久人妻中文字幕|