一步一步学Vue(九)

vue

一步一步学Vue(九)

2017-07-30 22:58 by JerremyZhang, ... 阅读, ... 评论, 收藏, 编辑

接上篇,这次是真的接上篇,针对上篇未完成的部分,增加鉴权功能,开始之前,我们先要介绍一个新的知识,路由元数据。

vue-router中,定义元数据的方式:

const router = new VueRouter({

routes: [

{

path: '/foo',

component: Foo,

children: [

{

path: 'bar',

component: Bar,

// a meta field

meta: { requiresAuth: true }

}

]

}

]

})

那么如何访问这个 meta 字段呢?

首先,我们把routes 配置中的每个路由对象叫做路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录

例如,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。

一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航钩子中的 route 对象)的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。

所以在vue-router官方文档中,我们可以看到下面的代码,其实就是前端路由授权的粗糙实现方式(代码不做过多解释,里面我加入了详细的注释):

router.beforeEach((to, from, next) => {

if (to.matched.some(record => record.meta.requiresAuth)) {

// 如果路由配置了元数据requiresAuth为true,则需要鉴权,这是需要判断是否登录

// 如果没有登录则跳转到login页面

if (!auth.loggedIn()) {

next({

path: '/login',

//这里传递fullPath,是为了登录之后作为return back

query: { redirect: to.fullPath }

})

} else {

//如果已经登录过,直接执行进入下一步

next()

}

} else {

//对没有配置requiresAuth的路由进行处理,如果不加入,则路由未配置requiresAuth,无法进入,所以确保一定要调用 next()

next()

}

})

好了,基础知识介绍完毕,现在我们把我们的路由加入meta信息,启用权限验证:

var router = new VueRouter({

routes: [{

name: 'home', path: '/home', component: HomeComponent

},

{

name: 'customers', path: '/customers', component: CustomerListComponent,

meta: {

auth: true

}

},

{

name: 'detail', path: '/detail/:id', component: CustomerComponent,

meta: {

auth: true

}

},

{

name: 'login', path: '/login', component: LoginComponent

}

]

});

//注册全局事件钩子

router.beforeEach(function (to, from, next) {

//如果路由中配置了meta auth信息,则需要判断用户是否登录;

if (to.matched.some(r => r.meta.auth)) {

//登录后会把token作为登录的标示,存在localStorage中

if (!localStorage.getItem('token')) {

console.log("需要登录");

next({

path: '/login',

query: { to: to.fullPath }

})

} else {

next();

}

} else {

next()

}

});

更新代码后,可以跟目录运行node app.js ,打开8110端口,查看,运行效果如下:

这个时候,无论从浏览器地址栏还是通过跳转方式,在点击配置了 meta:{auth:true}的路由时,如果没有登录,都会跳转到登录页面,并记录return back url。

下面我们加入登录逻辑,并修改后台接口,支持用户授权,后台我们使用jwt的一个实现https://github.com/auth0/node-jsonwebtoken ,直接使用npm 安装即可,对jwt不太了解的同学,可以搜索 json web token (jwt)(另外为了读取http body,我们这里会使用 body-parser,可以直接使用npm install --save body-parser 安装)。

首先修改我们的登录组件:

 methods: {

login: function () {

var self = this;

axios.post('/login', this.user)

.then(function (res) {

console.log(res);

if (res.data.success) {

localStorage.setItem('token', res.data.token);

console.log(self.$router);

self.$router.push({

path: self.$route.query.to

});

} else {

alert(res.data.errorMessage);

}

})

.catch(function (error) {

console.log(error);

});

}

}

并添加全局拦截器,在任何ajax请求中加入token 头,如果熟悉angular拦截器的同学对axios实现的拦截器应该很熟悉的,这和jquery 对Ajax.setting的设置类似:

// request 拦截器 ,对所有请求,加入auth

axios.interceptors.request.use(

cfg => {

// 判断是否存在token,如果存在,则加上token

if (localStorage.getItem('token')) {

cfg.headers.Authorization = localStorage.getItem('token');

}

return cfg;

},

err => {

return Promise.reject(err);

});

// http response 拦截器

axios.interceptors.response.use(

res => {

return res;

},

err => {

if (err.response) {

switch (err.response.status) {

case 401: //如果未授权访问,则跳转到登录页面

router.replace({

path: '/login',

query: {redirect: router.currentRoute.fullPath}

})

}

}

return Promise.reject(err.response.data)

});

这样,我们再每次请求的时候,如果token存在,则就会带上token;

接着,修改我们的后端部分,加入处理登录,以及生成解析token的部分,修改我们的authMiddleware.js文件:

var jwt = require('jsonwebtoken');

/**

* 有效用户列表

*/

var validUsers = [{

username: 'zhangsan',

password: '123456'

}, {

username: 'lisi',

password: '123456'

}];

//FIXME:这个作为**,一定要安全的,这里我为了简单就直接写了一大段字符串

const secretKey = 'dhahr3uiudfu93u43i3uy43&*&$#*&437hjhfjdjhfdfjsy8&*&*JNFSJDJHH??>:LP';

/**

* 创建token

* @param {用户对象} user

*/

var createToken = function (user) {

/**

* 创建token 并设置过期时间为一个小时

*/

return jwt.sign({ data: user, exp: Math.floor(Date.now() / 1000) + (60 * 60) }, secretKey);

}

/**

* 解析token

* @param {用户需要验证的token} token

*/

var parseToken = function (token, callback) {

jwt.verify(token, secretKey, function (err, result) {

callback && callback(err, result);

});

}

module.exports = function (req, res, next) {

//如果是登录请求

console.log(req.path);

if (req.path === "/login") {

var username = req.body.username;

var password = req.body.password;

//判断用户名和密码是否正确

var user = validUsers.filter(u => u.username === username && u.password === password)[0];

//如果用户用户名密码匹配成功,直接创建token并返回

if (user) {

res.json({

success: true,

token: createToken(user)

})

} else {

res.json({

success: false,

errorMessage: 'username or password is not correct,please retry again'

})

}

} else {//如果不是登录请求,则需要检查token 的合法性

var token = req.get('Authorization');

console.log(token);

if (token) {

parseToken(token, function (err, result) {

if (err) {//如果解析失败,则返回失败信息

res.status(401).json( {

success: false,

errorMessage: JSON.stringify(err)

})

} else {

next();

}

})

}else{

res.status(401).json({

success:false,

errorMessage:'未授权的访问'

});

}

}

}

上述代码加上注释应该没什么复杂度的,各位同学应该可以看的明白,这样之后,我们启用我们的授权中间件,修改/app.js文件:

var express = require("express");

var bodyParser = require("body-parser");

var authMiddleware = require('./middleware/authMiddleware');

var customerRouter = require('./router/customers');

var app = express();

app.use(express.static('public'));

app.get('/portal', function (req, res) {

res.json({

data: [

{

visits: 12,

clicks: 100

},

{

location: 'BeiJing',

total: 17

}

]

})

})

app.use(bodyParser.json())

app.use(authMiddleware);

app.use('/api', customerRouter);

运行我们的代码可以看到如下效果:

博客园对图片大小有要求,不能很好的截取,就只截取了一部分,这是登录后的效果,登录前的效果,大家可以自己测试,完整代码如下:

/app.js

var express = require("express");

var bodyParser = require("body-parser");

var authMiddleware = require('./middleware/authMiddleware');

var customerRouter = require('./router/customers');

var app = express();

app.use(express.static('public'));

app.get('/portal', function (req, res) {

res.json({

data: [

{

visits: 12,

clicks: 100

},

{

location: 'BeiJing',

total: 17

}

]

})

})

app.use(bodyParser.json())

app.use(authMiddleware);

app.use('/api', customerRouter);

app.listen(8110, function () {

console.log("port 8110 is listenning!!!");

});

View Code

/public/app.js

var LoginComponent = {

template: `

<div class="login" >

username:<input type="text" v-model="user.username" />

password:<input type="password" v-model="user.password" />

<input type="button" @click="login()" value="login" />

</div>

`,

data: function () {

return {

user: {

username: '',

password: ''

}

}

},

methods: {

login: function () {

var self = this;

axios.post('/login', this.user)

.then(function (res) {

console.log(res);

if (res.data.success) {

localStorage.setItem('token', res.data.token);

console.log(self.$router);

self.$router.push({

path: self.$route.query.to

});

} else {

alert(res.data.errorMessage);

}

})

.catch(function (error) {

console.log(error);

});

}

}

}

var CustomerListComponent = {

template: `

<div>

<div>

<input type="text" v-model="keyword" /> <input type="button" @click="getCustomers()" value="search" />

</div>

<ul>

<router-link v-for="c in customers" tag="li" :to="{name:'detail',params:{id:c.id}}" :key="c.id">{{c.name}}</router-link>

</ul>

</div>

`,

data: function () {

return {

customers: [],

keyword: ''

}

},

created: function () {

this.getCustomers();

},

methods: {

getCustomers: function () {

axios.get('/api/getCustomers', { params: { keyword: this.keyword } })

.then(res => { this.customers = res.data; console.log(res) })

.catch(err => console.log(err));

},

}

}

var CustomerComponent = {

template: `

<div>

{{customer}}

</div>

`,

data: function () {

return {

customer: {}

}

},

created: function () {

var id = this.$route.params.id;

this.getCustomerById(id);

},

watch: {

'$route': function () {

console.log(this.$route.params.id);

}

},

methods: {

getCustomerById: function (id) {

axios.get('/api/customer/' + id)

.then(res => this.customer = res.data)

.catch(err => console.log(err));

}

}

}

var HomeComponent = {

template: `<div>

<h1>Home 页面,portal页</h1>

<h2>以下数据来自服务端</h2>

{{stat}}

</div>`,

data: function () {

return {

stat: ''//代表相关统计信息等

}

},

methods: {

getStat: function () {

return axios.get('/portal');

}

},

created: function () {

this.getStat().then(res => {

this.stat = JSON.stringify(res.data);

}).catch(err => {

console.log(err);

})

}

}

var router = new VueRouter({

routes: [{

name: 'home', path: '/home', component: HomeComponent

},

{

name: 'customers', path: '/customers', component: CustomerListComponent,

meta: {

auth: true

}

},

{

name: 'detail', path: '/detail/:id', component: CustomerComponent,

meta: {

auth: true

}

},

{

name: 'login', path: '/login', component: LoginComponent

}

]

});

//注册全局事件钩子

router.beforeEach(function (to, from, next) {

//如果路由中配置了meta auth信息,则需要判断用户是否登录;

if (to.matched.some(r => r.meta.auth)) {

//登录后会把token作为登录的标示,存在localStorage中

if (!localStorage.getItem('token')) {

console.log("需要登录");

next({

path: '/login',

query: { to: to.fullPath }

})

} else {

next();

}

} else {

next()

}

});

// request 拦截器 ,对所有请求,加入auth

axios.interceptors.request.use(

cfg => {

// 判断是否存在token,如果存在,则加上token

if (localStorage.getItem('token')) {

cfg.headers.Authorization = localStorage.getItem('token');

}

return cfg;

},

err => {

return Promise.reject(err);

});

// http response 拦截器

axios.interceptors.response.use(

res => {

return res;

},

err => {

if (err.response) {

switch (err.response.status) {

case 401: //如果未授权访问,则跳转到登录页面

router.replace({

path: '/login',

query: {redirect: router.currentRoute.fullPath}

})

}

}

return Promise.reject(err.response.data)

});

var app = new Vue({

router: router,

template: `

<div>

<router-link :to="{name:'home'}" >Home</router-link>

<router-link :to="{name:'customers'}" >Customers</router-link>

<router-view></router-view>

</div>

`,

el: '#app'

});

View Code

/public/index.html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>demo3</title>

<script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>

<script src="https://cdn.bootcss.com/vue-router/2.7.0/vue-router.js"></script>

<script src="https://cdn.bootcss.com/axios/0.16.2/axios.js"></script>

</head>

<body>

<div id="app">

</div>

<script src="./app.js"></script>

</body>

</html>

View Code

/router/customers.js

var router = require("express").Router();

var db = require('./fakeData');

router.get('/getCustomers', function (req, res) {

var list = db.data;

list = list.filter(v => v.name.indexOf(req.query.keyword) !== -1);

res.json(list);

});

router.get('/customer/:id',function(req,res){

var list=db.data;

var obj=list.filter(v=>v.id==req.params.id)[0];

res.json(obj);

})

module.exports = router;

View Code

/router/fakeData.json

{

"data": [

{

"id":1,

"name": "zhangsan",

"age": 14,

"sexy": "男",

"majar": "学生"

},

{

"id":2,

"name": "lisi",

"age": 19,

"sexy": "女",

"majar": "学生"

},

{

"id":3,

"name": "wangwu",

"age": 42,

"sexy": "男",

"majar": "工人"

},

{

"id":4,

"name": "maliu",

"age": 10,

"sexy": "男",

"majar": "学生"

},

{

"id":5,

"name": "wangermazi",

"age": 82,

"sexy": "男",

"majar": "画家"

},

{

"id":6,

"name": "liudehua",

"age": 55,

"sexy": "男",

"majar": "天王"

},

{

"id":7,

"name": "zhoujielun",

"age": 14,

"sexy": "男",

"majar": "歌手"

},

{

"id":8,

"name": "wangfei",

"age": 50,

"sexy": "女",

"majar": "歌手"

},

{

"id":9,

"name": "mayun",

"age": 60,

"sexy": "男",

"majar": "老板"

}

]

}

View Code

/middleware/authMiddleware.js

var jwt = require('jsonwebtoken');

/**

* 有效用户列表

*/

var validUsers = [{

username: 'zhangsan',

password: '123456'

}, {

username: 'lisi',

password: '123456'

}];

//FIXME:这个作为**,一定要安全的,这里我为了简单就直接写了一大段字符串

const secretKey = 'dhahr3uiudfu93u43i3uy43&*&$#*&437hjhfjdjhfdfjsy8&*&*JNFSJDJHH??>:LP';

/**

* 创建token

* @param {用户对象} user

*/

var createToken = function (user) {

/**

* 创建token 并设置过期时间为一个小时

*/

return jwt.sign({ data: user, exp: Math.floor(Date.now() / 1000) + (60 * 60) }, secretKey);

}

/**

* 解析token

* @param {用户需要验证的token} token

*/

var parseToken = function (token, callback) {

jwt.verify(token, secretKey, function (err, result) {

callback && callback(err, result);

});

}

module.exports = function (req, res, next) {

//如果是登录请求

console.log(req.path);

if (req.path === "/login") {

var username = req.body.username;

var password = req.body.password;

//判断用户名和密码是否正确

var user = validUsers.filter(u => u.username === username && u.password === password)[0];

//如果用户用户名密码匹配成功,直接创建token并返回

if (user) {

res.json({

success: true,

token: createToken(user)

})

} else {

res.json({

success: false,

errorMessage: 'username or password is not correct,please retry again'

})

}

} else {//如果不是登录请求,则需要检查token 的合法性

var token = req.get('Authorization');

console.log(token);

if (token) {

parseToken(token, function (err, result) {

if (err) {//如果解析失败,则返回失败信息

res.status(401).json( {

success: false,

errorMessage: JSON.stringify(err)

})

} else {

next();

}

})

}else{

res.status(401).json({

success:false,

errorMessage:'未授权的访问'

});

}

}

}

View Code

/package.json

{

"name": "vue_demo3",

"version": "1.0.0",

"description": "",

"main": "app.js",

"scripts": {

"test": "echo \"Error: no test specified\" && exit 1"

},

"author": "",

"license": "ISC",

"dependencies": {

"body-parser": "^1.17.2",

"express": "^4.15.3",

"jsonwebtoken": "^7.4.1"

}

}

View Code

以上是 一步一步学Vue(九) 的全部内容, 来源链接: utcz.com/z/376550.html

回到顶部