vue登錄業務邏輯

原文鏈接:http://www.php.cn/js-tutorial-394777.html

這次給大家帶來vue2.0實現註冊登錄步驟詳解,vue2.0實現註冊登錄的注意事項有哪些,下面就是實戰案例,一起來看一下。

前言

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

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

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

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

系統環境:MacOS 10.13.3

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

使用淘寶鏡像安裝

1

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

然後所有的 npm install 改爲 cnpm install

項目流程圖

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

項目啓動

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

1

2

3

4

//在項目裏的mian.js裏增加下列代碼

import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

//login組件

<template>

 <p 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>

 </p>

</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-tabsitem {

 text-align: center;

 width: 60px;

}

</style>

接下來是註冊組件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

//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

引入

1

2

import Router from 'vue-router'

Vue.use(Router)

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

我們引入了三個組件:

HelloWorld 登錄後的展示頁

login 登錄主界面

register 註冊組件

路由守衛

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

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

1

2

//引入store

import store from './store'

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

1

2

3

//store index.js

import Vuex from 'vuex'

Vue.use(Vuex)

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

//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。

安裝

$ cnpm i -S axios

引入

import axios from 'axios'

攔截器

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

//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服務接口。

安裝

1

2

3

$ 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端的啓動入口。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

//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配置里加上:

1

2

3

4

5

6

7

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的方法。

安裝

1

2

3

4

5

6

7

8

$ 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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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,作爲數據庫連接入口。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//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,存放數據庫的操作方法。

先安裝一些功能插件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

$ 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的表單驗證後加上下列代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//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的表單驗證後加上下列代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

//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!

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

//Helloworld.vue

<template>

 <p 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>

 </p>

</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

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

axios出現302狀態碼怎麼處理

select內置組件使用詳解

以上就是vue2.0實現註冊登錄步驟詳解的詳細內容,更多請關注php中文網其它相關文章!

php中文網最新課程二維碼
  • 相關標籤:vue2.0 步驟 登錄
  • 本文原創發佈php中文網 ,轉載請註明出處,感謝您的尊重!