Vue Authentication And Route Handling Using Vue-router(详解)

英文原文  (本文原出处),本博在原文的基础上,进一步分析代码的结构和解释代码。

git 代码

创建一个app:  vue-router-auth

本文详解了如何使用vue-router建立路由记录对路由记录进行检测,以及路由的处理。

1.如何使用vue-router来定义检测我们的routes,防止用户进入某个路由。

2.基于验证状态,如何跳转用户到app的不同部分。

3.用Node.js建立了一个mini server 来处理用户验证

安装Vue cli3

vue ui 

插件选择vue-router。

 

步骤:


 

建立Node.js Server

//安装数据库
npm install --save sqlite3 // hash passwords npm install --save bcrypt //An implementation of JSON Web Tokens. npm install jsonwebtoken //用于读json数据。 npm install --save body-parser

后续(具体见英文原文)

现在创建一个nodejs server用于处理user authentication.

创建一个新目录server. 它用于储存所有的使node backend的文件。

1.

创建app.js, 增加配置代码。(点击连接)

 

引进需要的package, 定义database, 创建一个express server和express router。

然后,定义CORS middleware, ensure we do not run into any cross origin resource errors

然后,定义路径,用于注册一个新的用户

router.post('/register', function(req, res) {...

//  传递请求body到一个databae method, 并传递一个回调函数来处理从数据库来的response data
//  定义了error checks来确保提供准确的信息给user.

 

当一个user成功注册,我们选择用用户的emai,创建一个验证token。(会用到之前安装的包jwt)

我们使用secret key在配置文件用来sign the auth credentials。

然后,定义路径,注册一个管理员,并登入。

router.post('/register-admin', function(req, res) {
//
}

router.post('/login', (req, res) => {
//
}

 

对login, 使用了bcrypt来判断user的password。

使用express server让app 可用accessible.

这里创建了一个server,端口是3000.

(以上内容需要学习node.js的相关知识)

 

2. 创建config.js  (见代码)

3. 创建db.js。 这里配置数据库,创建一个新数据库,并创建CRUD数据的方法。


 

更新vue-router文件

routes.js (点击查看代码)

首先确认要app的pages:

首页,注册页,登陆页,用户个人版,管理员版。

import HelloWorld from '@/components/HelloWorld'
import Login from '@/components/Login'
import Register from '@/components/Register'
import UserBoard from '@/components/UserBoard'
import Admin from '@/components/Admin'

 

 

然后,定义router构造器,使用routes构建选项,为每个组件添加路由:

let router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
//后续略,见git。

 

这里使用了meta field用于自定义额外的行为。见👇的知识点

 

之后,使用全局前置守卫,对路由的导航进行检测:

通过检测的继续,不通过检测的canceling或者跳转redirecting。

  • 如果route requiresAuth, 检查a jwt token, 它代表user是登陆的。
  • 如果route requiresAuth 并且 只能admin users使用,则检查auth和检查用户是否是admin
  • 如果route 需要 guest, 检查用户是否登陆。

看代码:

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
  //第一块
  } else if (to.matched.some(record => record.meta.guest)) {
   //第二块
  } else {
    next()
  }
})

 

 

routes.js中,检查是在to对象上。

  • if检测route requiresAuth。本app中是导向/admin和/dashboard
  • else if检测route是否requires guest. 本app导向注册和登陆的路由。
  • 都不需要则,next(),本app是指导向首页的路由。

第一块if内部代码:

因为要求user必须登陆才能访问网页,所以加入if检测:

  • 如果之前user已经登陆/注册,则继续:
    • 因为/admin,需要管理员才能进入,所以继续判断,要导航到的路由是否需要admin
      • 是,   再判断user是否是admin:
        • 是,   进入/admin, 即next()。
        • 不是,进入/dashboard。
      • 不是,next()
  • 如果未登陆,则跳转到登陆页面/login
    if (localStorage.getItem('jwt') == null) {
      next({
        path: '/login',
        // 参数用于完成登陆后,要跳转的URl。
        params: {nextUrl: to.fullPath}
      })
    } else {
      let user = JSON.parse(localStorage.getItem('user'))
      if (to.matched.some(record => record.meta.is_admin)) {
        // 如果要进入管理员页面,则判断user是否是管理员
        if (user.is_admin == 1) {
          next()
        } else {
          next({name; 'UserBoard'})
        }
      } else {
        next()
      }
    }

 

第二块内部代码:

因为路由的meta标记了可以guest访问这个URL,即user无需登陆即可访问。

这里加入嵌套的if检测:

  • 如果之前user已经登陆/注册,则跳转redirect到URL: /dashboard
  • 如果访问者未登陆,则next().
    if (localStorage.getItem('jwt') == null ) {
      next()
    } else {
      next({ name: 'UserBoard'})   //这里使用name来跳转。
    }

 


 

知识点:

Navigation Guards

Navigation指正路由正在发送改变。

 

Global Guard注册一个全局前置守卫:

 

当一个navigation(链接)被激活,全局前置守卫按照创建顺序被调用。

Guards是异步地resolved解析执行。 

在所有的hooks被resovled前,navigation(导航)一直会处于pending(等待中)。

  •  to是将要导航到的目标route object。
  •  from是当前的路由,正准备离开。
  •  next是回调函数。为了resolve the hook, next函数必须被调用。
    • next(): 进行管道中的下一个hook,当全部hook执行完成,导航的状态就是confirmed。

 

一个导航的流程flow:

导航被触发。
在失活的组件里调用离开守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

 

meta:  路由元信息

定义路由的时候可以配置a meta field。

Vue router allows use define a meta on our routes so we can specify additional behaviour. 

routes: [
        {
            path: '/admin',
            name: 'admin',
            component: Admin,
            meta: { 
                requiresAuth: true,
                is_admin : true
            }
        },
]

如何访问meta field?

在路由配置中,每个路由对象route object就叫做路由记录route record

route record是可以嵌套的。

因此当一个route被匹配时,它可以有多个route record。

比如URL /foo/bar, 可以匹配父路由记录(/foo)也同时匹配子路由记录(/foo/bar)

 

使用$route.matched可以得到当前路由对象的所有匹配的路由记录,

因此,通过遍历$route.matched来检查路由记录中的meta filed。

 

路由对象 (点击)

一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录 (route records)。

每次成功的导航后都会产生一个新的对象。

Route Object Properties:

$route.path

$route.params   类型是Object.

$route.query 

一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。

$route.matched, 类型Array<RouteRecord>

一个数组,包含当前路由的所有嵌套路径片段的路由记录 。

比如URL /foo/bar,  $route.matched得到的数组包含父路由记录,也包含子路由记录。

 

this.$route表示当前激活的路由信息对象,只读的属性。

this.$router是router实例。

 

Array.some(func) 

执行内部函数,每个元素需要通过这个函数,如果发现一个元素满足函数,返回true, some()返回true.


 

 

Define Some Components

修改HelloWorld.vue组件。

 

创建Login.vue

⚠️:自定义事件handleSubmit()

this.$http: 实际是使用了Axios。并进行了设置。

发出POST, 并传递一个对象参数:包括email和password。

  • 这里数据email和password和input标签双向绑定了v-model。
  • 另外,如果form是一个很大的表格,也可以使用formData构造器来传递对象参数。
      handleSubmit(e) {
        e.preventDefault()
        if ( this.password.length > 0 ) {
          this.$http.post('http://localhost:3000/login', {
            email: this.email,
            password: this.password
          })
          .then(response => {//...})
          .catch(function(error){
            console.error(error.response)
          })

再写.then(response => {//...})

  • 首先,把从server传回的数据jwt token和user信息,储存在localStorage。
  • 然后,根据用户是否成功登陆,和user是否是管理员,进行不同的路由的跳转。
    • 有jwt,代表user有登陆权限,下一步是登陆,和后续判断。
      • 如果当前路由的this.$route有nextUrl参数则跳转到这里,使用router.push(location)
      • 否则,根据用户是否有管理员身份跳转到不同的页面。
          .then(response => {
            let is_admin = response.data.user.is_admin
            localStorage.setItem('user', JSON.stringify(response.data.user))
            localStorage.setItem('jwt', response.data.token)

            if (localStorage.getItem('jwt') != null) {
              this.$emit('loggedIn')
              if (this.$route.params.nextUrl != null) {
                this.$router.push(this.$route.params.nextUrl)
              } else {
                if (is_admin == 1) {
                  this.$router.push('admin')
                } else {
                  this.$router.push('dashboard')
                }
              }
            }
          })

之后就可以访问app的各个部分了。

 

创建Register.vue文件

这里使用了vee-validate依赖。在客户端对用户输入进行检验:

部分代码:密码的存在验证和confirmed验证:

重点:使用ref

      <div>
        <label for="password">Password</label>
        <input v-validate="'required'" name="password" type="password" :class="{'is-danger': errors.has('password')}" ref="password">
        <span v-show="errors.has('password')" class="help is-danger">{{ errors.first('password') }}</span>
      </div>
      <div>
        <label for="password-confirm">Confirm Password</label>
        <input v-validate="'required|confirmed:password'" name="password_confirmation" type="password" :class="{'is-danger': errors.has('password_confirmation')}" placeholder="Password, Again" data-vv-as="password">
        <span v-show="errors.has('password_confirmation')">{{errors.first('password_confirmation')}}</span>
      </div>

 

event:

      handleSubmit(e) {
        e.preventDefault()
        this.$validator.validateAll().then((result) => {
          if (result) {
       //通过验证后的代码
          } else {
            console.log("Confirm the errors")
          }
        })
      },

 

具体见git

 

之后写Admin.vue和UserBoard.vue,很简单,代码见git。

 

Setting Up Axios Globally

npm install axios

 

然后在/src/main.js,引进

import Axios from 'axios'

Vue.prototype.$http = Axios;  #替代Axios的写法

 

Running The Application

打开package.json,然后替换原来的"scripts".

增加server 的script来启动node server。

  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "server": "node server/app",
    "build": "node build/build.js"
  },

 

 master± ⮀ npm run server

> vue-router-auth@0.1.0 server /Users/chen/vue-router-auth
> node server/app

Express server listening on port 3000

 

 最后在另一个terminal,输入命令npm run dev。

提示❌:

$ webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
The CLI moved into a separate package: webpack-cli

Please install 'webpack-cli' in addition to webpack itself to use the CLI
-> When using npm: npm i -D webpack-cli
-> When using yarn: yarn add -D webpack-cli

 

备注:

yarn add -D webpack-cli

暂时未能修复问题

 

posted @ 2018-12-21 16:16  Mr-chen  阅读(642)  评论(0编辑  收藏  举报