<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題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關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
        當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

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

        來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:16:34
        文檔

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

        vue2.0+koa2+mongodb實(shí)現(xiàn)注冊(cè)登錄:前言 前段時(shí)間和公司一個(gè)由技術(shù)轉(zhuǎn)產(chǎn)品的同事探討他的職業(yè)道路,對(duì)我說(shuō)了一句深以為然的話: 不要把自己禁錮在某一個(gè)領(lǐng)域,技術(shù)到產(chǎn)品的轉(zhuǎn)變,首先就是思維上的轉(zhuǎn)變。你一直做前端,數(shù)據(jù)的交互你只知道怎么進(jìn),卻不知道里面是怎么出的,這就是局限性。 醍
        推薦度:
        導(dǎo)讀vue2.0+koa2+mongodb實(shí)現(xiàn)注冊(cè)登錄:前言 前段時(shí)間和公司一個(gè)由技術(shù)轉(zhuǎn)產(chǎn)品的同事探討他的職業(yè)道路,對(duì)我說(shuō)了一句深以為然的話: 不要把自己禁錮在某一個(gè)領(lǐng)域,技術(shù)到產(chǎn)品的轉(zhuǎn)變,首先就是思維上的轉(zhuǎn)變。你一直做前端,數(shù)據(jù)的交互你只知道怎么進(jìn),卻不知道里面是怎么出的,這就是局限性。 醍

        前言

        前段時(shí)間和公司一個(gè)由技術(shù)轉(zhuǎn)產(chǎn)品的同事探討他的職業(yè)道路,對(duì)我說(shuō)了一句深以為然的話:

        “不要把自己禁錮在某一個(gè)領(lǐng)域,技術(shù)到產(chǎn)品的轉(zhuǎn)變,首先就是思維上的轉(zhuǎn)變。你一直做前端,數(shù)據(jù)的交互你只知道怎么進(jìn),卻不知道里面是怎么出的,這就是局限性。”

        醍醐灌頂般,剛好學(xué)習(xí)vue的時(shí)候看到有個(gè)注冊(cè)登錄的項(xiàng)目,索性我也跟著動(dòng)手做一個(gè)vue項(xiàng)目,引入koa和mongodb,實(shí)現(xiàn)客戶端(client)提交-服務(wù)端(server)接收返回-入數(shù)據(jù)庫(kù)全過(guò)程。

        本項(xiàng)目基于vue-cli搭建,利用token方式進(jìn)行用戶登錄驗(yàn)證,并實(shí)現(xiàn)注冊(cè)入庫(kù)、讀取用戶、刪除用戶等功能。文章默認(rèn)讀者有一定的node和vue基礎(chǔ),基礎(chǔ)部分不贅述。

        系統(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)目流程圖

        為了讓項(xiàng)目思路和所選技術(shù)更加清晰明了,畫了一個(gè)圖方便理解。

         

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

        0.項(xiàng)目地址

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

        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="名稱" 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)輸入您的名稱', 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="名稱" 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)輸入您的名稱', 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) => {
         //把用戶名存起來(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的更改、清除以及用戶名儲(chǔ)存的操作。

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

         

        我們寫好了基礎(chǔ)界面,接下來(lái)就是要把表單數(shù)據(jù)發(fā)送到后臺(tái)并進(jìn)行一系列處理。現(xiàn)在還沒(méi)有后端接口沒(méi)關(guān)系,我們先寫好前端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客戶端,適用于:瀏覽器和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)單的前端路由控制,并不能真正阻止用戶訪問(wèn)需要登錄權(quán)限的路由。當(dāng)token失效了,但token依然保存在本地。這時(shí)候你去訪問(wèn)需要登錄權(quán)限的路由時(shí),實(shí)際上應(yīng)該讓用戶重新登錄。這時(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 {
         //用戶注冊(cè)
         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);
         }
        }

        代碼最后暴露了四個(gè)請(qǐng)求方法,分別對(duì)應(yīng)注冊(cè)(register)、登錄(login)、獲取(user)、刪除(delUser)用戶,并且都在/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è)方法寫相對(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à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);
        
        //獲取所有用戶
        const userRouter = new Router();
        userRouter.get('/user', checkToken, UserController.GetAllUsers);
        //刪除某個(gè)用戶
        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);
        });

        代碼里可以看到,獲取用戶和刪除用戶都需要驗(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、客戶端通過(guò)用戶名和密碼登錄服務(wù)器;

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

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

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

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

        6、服務(wù)端收到請(qǐng)求后,首先驗(yàn)證Token,之后返回?cái)?shù)據(jù)。服務(wù)端不需要保存Token,只需要對(duì)Token中攜帶的信息進(jìn)行驗(yàn)證即可。無(wú)論客戶端訪問(wèn)后臺(tái)的哪臺(tái)服務(wù)器,只要可以通過(guò)用戶信息的驗(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í),我們把用戶名作為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定義表的模板,讓這一類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表,后面寫注冊(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ù)用戶名查找用戶
        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);
         });
         });
        };
        //刪除某個(gè)用戶
        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 ) => {
         //拿到賬號(hào)和密碼
         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('密碼一致!');
        
         //生成一個(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)換為用戶創(chuàng)建時(shí)間
         });
         //將objectid轉(zhuǎn)換為用戶創(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('用戶名已經(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
         }
         }
        };
        //獲得所有用戶信息
        const GetAllUsers = async( ctx ) => {
         //查詢所有用戶信息
         let doc = await findAllUsers();
         ctx.status = 200;
         ctx.body = {
         succsess: '成功',
         result: doc
         };
        };
        
        //刪除某個(gè)用戶
        const DelUser = async( ctx ) => {
         //拿到要?jiǎng)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è)方法

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

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

        登錄方法

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

        獲得所有用戶信息

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

        刪除某個(gè)用戶

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

        寫完這些方法,就可以把前面沒(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)的密碼是由于哈希加密編譯而成。

         

        整合完善注冊(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: '用戶名已經(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è)用戶名,并給出刪除按鈕。

        //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){
         //不成功跳轉(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: '注銷成功'
         })
         } 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>

        輸出頁(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 要寫成對(duì)象格式,否則會(huì)報(bào)錯(cuò)

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

        總結(jié)

        人的思維轉(zhuǎn)變確實(shí)是最難的。按流程來(lái)說(shuō),應(yīng)該是koa先設(shè)計(jì)出接口,前端再根據(jù)這個(gè)接口去請(qǐng)求,但我反過(guò)來(lái),是先寫好前端請(qǐng)求,再根據(jù)這個(gè)請(qǐng)求去制定接口。

        當(dāng)然,也遇到了很多困難:當(dāng)我搞好了前端展示頁(yè)面,axios也寫好了,但在用koa寫接口這里卡了很久,完全沒(méi)有概念,就是前言說(shuō)的“只知道數(shù)據(jù)怎么進(jìn),不知道怎么出”。然后遇到接口500報(bào)錯(cuò)又調(diào)試了很久,主要是自己對(duì)接口沒(méi)有調(diào)試概念,最后還是公司的瑯琊大佬幫忙解決,感謝。

        聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

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

        vue2.0+koa2+mongodb實(shí)現(xiàn)注冊(cè)登錄:前言 前段時(shí)間和公司一個(gè)由技術(shù)轉(zhuǎn)產(chǎn)品的同事探討他的職業(yè)道路,對(duì)我說(shuō)了一句深以為然的話: 不要把自己禁錮在某一個(gè)領(lǐng)域,技術(shù)到產(chǎn)品的轉(zhuǎn)變,首先就是思維上的轉(zhuǎn)變。你一直做前端,數(shù)據(jù)的交互你只知道怎么進(jìn),卻不知道里面是怎么出的,這就是局限性。 醍
        推薦度:
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 国产一卡2卡3卡4卡2021免费观看| 黑人粗长大战亚洲女2021国产精品成人免费视频| 国产亚洲精品无码成人| 最近最新高清免费中文字幕 | 日韩高清在线免费看| 一区二区三区精品高清视频免费在线播放| 国产成人精品日本亚洲网站| 91情侣在线精品国产免费| 一个人免费观看www视频| 亚洲欧洲精品在线| 亚洲人成人网站在线观看| 97碰公开在线观看免费视频| 在线观看亚洲免费| 91久久亚洲国产成人精品性色| 日本高清免费不卡在线| 一级毛片免费播放| 黄网站在线播放视频免费观看| 亚洲色大成网站www永久| 人人狠狠综合久久亚洲高清| 曰曰鲁夜夜免费播放视频| 亚洲一级片免费看| 亚洲精品欧美综合四区| 亚洲免费视频网站| 久久亚洲中文字幕精品一区四| 毛片免费观看网址| 最好看最新的中文字幕免费| 成人免费夜片在线观看| 亚洲欧美aⅴ在线资源| 亚洲人成电影在在线观看网色| 亚洲精品国产电影| 午夜一级免费视频| 国产精品色拉拉免费看| 成人性生交大片免费看中文| 青娱乐在线视频免费观看| 国产成人亚洲综合网站不卡| 亚洲网址在线观看| 亚洲国产精品自在在线观看| 亚洲一区二区三区无码影院| 国产一精品一aⅴ一免费| 欧洲精品成人免费视频在线观看| 日韩午夜理论免费TV影院|