Learn Vue Step by Step (IX)
Picking up where we left off, this time literally picking up where we left off, adding forensics for the unfinished parts of the previous post, before we start, we'll introduce a new piece of knowledge, routing metadata.
In vue-router, the metadata is defined in the following way.
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ] })
So how do you access thismeta What about the fields?
First, we putroutes Each routing object in the configuration is called a routing record. Routing records can be nested, so that when a route matches successfully, he may match multiple routing records
For example, according to the routing configuration above, the/foo/bar This URL will match the parent routing record as well as the child routing record.
All route records matched by a route are exposed as$route object (and the route object in the navigation hook) of the$route.matched Number group. Therefore, we need to traverse the$route.matchedto check the routing records in ofmeta Fields.
So invue-router Official Documents in, We can see the following code, It's actually a crude implementation of front-end routing authorization( The code is not explained too much, I'm in there. join The detailed annotation of the):
router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // If the route is configured with the metadata requiresAuth to be true, then authentication is required, which is needed to determine if you are logged in // If not logged in then jump tologin pages if (!auth.loggedIn()) { next({ path: '/login', // Passing fullPath here is meant to be used as a return back after logging in query: { redirect: to.fullPath } }) } else { // If you are already logged in, go directly to the next step next() } } else { // For unconfiguredrequiresAuth The routing of the, If not join, then the route is not configuredrequiresAuth, inaccessible, So make sure to always call next() next() } })
okay, The basics are covered, Now we take our routing joinmeta information, Enable permission verification:
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 } ] });
// Registering global event hooks router.beforeEach(function (to, from, next) { // If the route is configured withmeta auth information, then you need to determine if the user is logged in; if (to.matched.some(r => r.meta.auth)) { // Logging in will puttoken As a sign of login, presencelocalStorage in if (!localStorage.getItem('token')) { console.log(" Login required"); next({ path: '/login', query: { to: to.fullPath } }) } else { next(); } } else { next() } });
After updating the code, you can run node app.js with the directory ,open port 8110, check it out and it will run as follows.
This time, whether from the browser address bar or via a jump, when clicking on a route with meta:{auth:true} configured, it will jump to the login page and log the return back url if it is not logged in.
Below we join Login Logic, and modify the backend interface, Support for user authorization, In the background we usejwt An implementation of thehttps://github.com/auth0/node-jsonwebtoken , Direct usenpm Just install it., rightjwt Students who don't know much, Searchable json web token (jwt)( Also in order to readhttp body, We will use here body-parser, Can be used directlynpm install --save body-parser erect)。
First modify our login component to.
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); }); } }
and add the global interceptor, In anyajax requesting in jointoken first, If one is familiar withangular interceptor of the students were interested inaxios realized interceptor Should be familiar., This is the same asjquery rightAjax.setting The settings are similar to:
// request interceptor , For all requests, joinauth axios.interceptors.request.use( cfg => { // Determining the presence or absence oftoken, If there is, then addtoken if (localStorage.getItem('token')) { cfg.headers.Authorization = localStorage.getItem('token'); } return cfg; }, err => { return Promise.reject(err); }); // http response interceptor axios.interceptors.response.use( res => { return res; }, err => { if (err.response) { switch (err.response.status) { case 401: // If unauthorized access to, then it jumps to the login page router.replace({ path: '/login', query: {redirect: router.currentRoute.fullPath} }) } } return Promise.reject(err.response.data) });
thus, We then each time we request, in casetoken presence, then it will taketoken;
follow, Modifying our back end section, join Handling logins, and the generation of parsestoken part of, Modify ourauthMiddleware.js documents:
var jwt = require('jsonwebtoken'); /** * List of active users */ var validUsers = [{ username: 'zhangsan', password: '123456' }, { username: 'lisi', password: '123456' }]; //FIXME:This is used as a key, it must be secure, here I just wrote a big string for simplicity const secretKey = 'dhahr3uiudfu93u43i3uy43&*&$#*&437hjhfjdjhfdfjsy8&*&*JNFSJDJHH??>:LP'; /** * Create token * @param {user object} user */ var createToken = function (user) { /** * Create a token and set the expiration time to one hour */ return jwt.sign({ data: user, exp: Math.floor(Date.now() / 1000) + (60 * 60) }, secretKey); } /** * Parsing token * @param {token that the user needs to authenticate} token */ var parseToken = function (token, callback) { jwt.verify(token, secretKey, function (err, result) { callback && callback(err, result); }); } module.exports = function (req, res, next) { // If it is a login request console.log(req.path); if (req.path === "/login") { var username = req.body.username; var password = req.body.password; // Determine if the username and password are correct var user = validUsers.filter(u => u.username === username && u.password === password)[0]; // If the user username password match is successful, create the token directly and return 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 {//If it's not a login request, you need to check the token's legitimacy var token = req.get('Authorization'); console.log(token); if (token) { parseToken(token, function (err, result) { if (err) {//If parsing fails, return a failure message res.status(401).json( { success: false, errorMessage: JSON.stringify(err) }) } else { next(); } }) }else{ res.status(401).json({ success:false, errorMessage:' Unauthorized access' }); } } }
The above code with comments should be of little complexity and you should be able to read it, so after that, we enable our authorization middleware and modify the /app.js file to.
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);
Running our code shows the following effect.
Blogland has requirements for the size of the image, can not be well intercepted, it is only part of the interception, this is the effect after logging in, the effect before logging in, you can test yourself, the full code is as follows.
/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!!!"); });
/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 pages,portal leaf</h1> <h2> The following data is from the server side</h2> {{stat}} </div>`, data: function () { return { stat: ''// Representation of relevant statistical information, etc. } }, 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 } ] }); // Registering global event hooks router.beforeEach(function (to, from, next) { // If the route is configured withmeta auth information, then you need to determine if the user is logged in; if (to.matched.some(r => r.meta.auth)) { // Logging in will puttoken As a sign of login, presencelocalStorage in if (!localStorage.getItem('token')) { console.log(" Login required"); next({ path: '/login', query: { to: to.fullPath } }) } else { next(); } } else { next() } }); // request interceptor , For all requests, joinauth axios.interceptors.request.use( cfg => { // Determining the presence or absence oftoken, If there is, then addtoken if (localStorage.getItem('token')) { cfg.headers.Authorization = localStorage.getItem('token'); } return cfg; }, err => { return Promise.reject(err); }); // http response interceptor axios.interceptors.response.use( res => { return res; }, err => { if (err.response) { switch (err.response.status) { case 401: // If unauthorized access to, then it jumps to the login page 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' });
/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>
/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;
/router/fakeData.json
{ "data": [ { "id":1, "name": "zhangsan", "age": 14, "sexy": " male", "majar": " students" }, { "id":2, "name": "lisi", "age": 19, "sexy": " women", "majar": " students" }, { "id":3, "name": "wangwu", "age": 42, "sexy": " male", "majar": " workers" }, { "id":4, "name": "maliu", "age": 10, "sexy": " male", "majar": " students" }, { "id":5, "name": "wangermazi", "age": 82, "sexy": " male", "majar": " painter" }, { "id":6, "name": "liudehua", "age": 55, "sexy": " male", "majar": " emperor" }, { "id":7, "name": "zhoujielun", "age": 14, "sexy": " male", "majar": " vocalists" }, { "id":8, "name": "wangfei", "age": 50, "sexy": " women", "majar": " vocalists" }, { "id":9, "name": "mayun", "age": 60, "sexy": " male", "majar": " honcho" } ] }
/middleware/authMiddleware.js
var jwt = require('jsonwebtoken'); /** * List of active users */ var validUsers = [{ username: 'zhangsan', password: '123456' }, { username: 'lisi', password: '123456' }]; //FIXME:This is used as a key, it must be secure, here I just wrote a big string for simplicity const secretKey = 'dhahr3uiudfu93u43i3uy43&*&$#*&437hjhfjdjhfdfjsy8&*&*JNFSJDJHH??>:LP'; /** * Create token * @param {user object} user */ var createToken = function (user) { /** * Create a token and set the expiration time to one hour */ return jwt.sign({ data: user, exp: Math.floor(Date.now() / 1000) + (60 * 60) }, secretKey); } /** * Parsing token * @param {token that the user needs to authenticate} token */ var parseToken = function (token, callback) { jwt.verify(token, secretKey, function (err, result) { callback && callback(err, result); }); } module.exports = function (req, res, next) { // If it is a login request console.log(req.path); if (req.path === "/login") { var username = req.body.username; var password = req.body.password; // Determine if the username and password are correct var user = validUsers.filter(u => u.username === username && u.password === password)[0]; // If the user username password match is successful, create the token directly and return 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 {//If it's not a login request, you need to check the token's legitimacy var token = req.get('Authorization'); console.log(token); if (token) { parseToken(token, function (err, result) { if (err) {//If parsing fails, return a failure message res.status(401).json( { success: false, errorMessage: JSON.stringify(err) }) } else { next(); } }) }else{ res.status(401).json({ success:false, errorMessage:' Unauthorized access' }); } } }
/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" } }