vue 使用路由器
Vue is a progressive Javascript framework that makes building frontend applications easy. Coupled with vue-router, we can build high performance applications with complete dynamic routes. Vue-router is an efficient tool and can handle authentication in our Vue application seamlessly. In this tutorial, we will look at using vue-router to handle authentication and access control for different parts of our application.
Vue是一个渐进式Javascript框架,可简化构建前端应用程序的过程。 结合vue-router ,我们可以构建具有完整动态路由的高性能应用程序。 Vue路由器是一种高效的工具,可以在我们的Vue应用程序中无缝处理身份验证。 在本教程中,我们将研究如何使用vue-router处理应用程序不同部分的身份验证和访问控制。
To begin, install Vue cli and create a vue application with it:
首先,安装Vue cli并使用它创建一个vue应用程序:
$npm install -g @vue/cli
$ npm install -g @vue/cli-init
$ vue init webpack vue-router-auth
Follow the setup prompt and complete the installation of this application. If you are not sure of an option, simply click the return key (enter key) to continue with the default option. When asked to install vue-router, accept the option, because we need vue-router for this application.
遵循安装提示并完成此应用程序的安装。 如果不确定选项,只需单击返回键(输入键)以继续使用默认选项。 当要求安装vue-router时,请接受该选项,因为我们需要此应用程序的vue-router。
Next, we will setup a Node.js server that would handle authentication for us. For our Node.js server, we will use SQLite as the database of choice. Run the following command to install SQLite driver:
接下来,我们将设置一个Node.js服务器来为我们处理身份验证。 对于我们的Node.js服务器,我们将使用SQLite作为选择的数据库。 运行以下命令以安装SQLite驱动程序:
$npm install --save sqlite3
Because we are dealing with passwords, we need a way to hash passwords. We will use bcrypt to hash all our passwords. Run the following command to install it:
因为我们正在处理密码,所以我们需要一种哈希密码的方法。 我们将使用bcrypt哈希所有密码。 运行以下命令进行安装:
$npm install --save bcrypt
We also want a way to confirm the user’s we authenticate when they try to make a request to a secured part of our application. For this, we will use JWT. Run the following command to install the JWT package we will use:
我们还希望有一种方法可以在用户尝试向我们的应用程序的安全部分发出请求时确认我们的身份。 为此,我们将使用JWT 。 运行以下命令以安装我们将使用的JWT软件包:
$npm install jsonwebtoken --save
To read json
data we will send to our server, we need body-parser
. Run the following command to install it:
要读取将发送到服务器的json
数据,我们需要body-parser
。 运行以下命令进行安装:
$npm install --save body-parser
Now that it’s all set, let us create a simple nodejs server that would handle user authentication. Create a new directory named server. This is where we will store everything we will use to make our node backend. In the server directory, create a file and save it as app.js
. Add the following to it:
至此,我们创建了一个简单的nodejs服务器来处理用户身份验证。 创建一个名为server的新目录。 在这里,我们将存储用于制作节点后端的所有内容。 在服务器目录中,创建一个文件并将其另存为app.js
向其中添加以下内容:
"use strict";
const express = require('express');
const DB = require('./db');
const config = require('./config');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const db = new DB("sqlitedb")
const app = express();
const router = express.Router();
router.use(bodyParser.urlencoded({
extended: false }));
router.use(bodyParser.json());
We have required all the packages we need for our application, defined the database, created an express server and an express router.
我们需要应用程序所需的所有程序包,定义数据库,创建快递服务器和快递路由器。
Now, let's define CORS middleware, to ensure we do not run into any cross origin resource errors:
现在,让我们定义CORS中间件,以确保我们不会遇到任何跨源资源错误:
// CORS middleware
const allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', '*');
res.header('Access-Control-Allow-Headers', '*');
next();
}
app.use(allowCrossDomain)
Many people would use a CORS package here, but we do not have any complicated configurations, so this is fine.
很多人会在这里使用CORS软件包,但是我们没有任何复杂的配置,所以很好。
Let's define the route for registering a new user:
让我们定义注册新用户的途径:
router.post('/register', function(req, res) {
db.insert([
req.body.name,
req.body.email,
bcrypt.hashSync(req.body.password, 8)
],
function (err) {
if (err) return res.status(500).send("There was a problem registering the user.")
db.selectByEmail(req.body.email, (err,user) => {
if (err) return res.status(500).send("There was a problem getting user")
let token = jwt.sign({
id: user.id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
});
res.status(200).send({
auth: true, token: token, user: user });
});
});
});
There are a few things happening here. First, we pass the request body to a database method (which we will define later), and pass a callback function that to handle the response from the database operation. As expected, we have defined error checks to ensure we provide accurate information ot users.
这里发生了一些事情。 首先,我们将请求主体传递给数据库方法(稍后将进行定义),然后传递一个回调函数来处理数据库操作的响应。 与预期的一样,我们定义了错误检查以确保我们为用户提供准确的信息。
When a user is successfully registered, we select the user data by email and create an authentication token for the user with the jwt
package we had imported earlier. We use a secret key in our config file (which we will create later) to sign the auth credentials. This way, we can verify a token sent to our server and a user cannot fake an identity.
成功注册用户后,我们通过电子邮件选择用户数据,并使用我们先前导入的jwt
包为该用户创建身份验证令牌。 我们在配置文件(稍后将创建)中使用密钥来对身份验证凭据进行签名。 这样,我们可以验证发送到我们服务器的令牌,并且用户无法伪造身份。
Now, define the route for registering an administrator and logging in, which are similar to register:
现在,定义注册管理员和登录的路由,与注册类似:
router.post('/register-admin', function(req, res) {
db.insertAdmin([
req.body.name,
req.body.email,
bcrypt.hashSync(req.body.password, 8),
1
],
function (err) {
if (err) return res.status(500).send("There was a problem registering the user.")
db.selectByEmail(req.body.email, (err,user) => {
if (err) return res.status(500).send("There was a problem getting user")
let token = jwt.sign({
id: user.id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
});
res.status(200).send({
auth: true, token: token, user: user });
});
});
});
router.post('/login', (req, res) => {
db.selectByEmail(req.body.email, (err, user) => {
if (err) return res.status(500).send('Error on the server.');
if (!user) return res.status(404).send('No user found.');
let passwordIsValid = bcrypt.compareSync(req.body.password, user.user_pass);
if (!passwordIsValid) return res.status(401).send({
auth: false, token: null });
let token = jwt.sign({
id: user.id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
});
res.status(200).send({
auth: true, token: token, user: user });
});
})
For login, we use bcrypt to compare our hashed password with the user supplied password. If they are the same, we log the user in. If not, well, feel free to respond to the user how you please.
对于登录,我们使用bcrypt将哈希密码与用户提供的密码进行比较。 如果它们相同,我们将使用户登录。如果不是,那么,请随时响应用户的要求。
Now, let's use the express server to make our application accessible:
现在,让我们使用快递服务器使我们的应用程序可访问:
app.use(router)
let port = process.env.PORT || 3000;
let server = app.listen(port, function() {
console.log('Express server listening on port ' + port)
});
We created a server on port: 3000
or any dynamically generated port by our system (heroku generates dynamic ports).
我们在以下port: 3000
上创建了一个服务器port: 3000
或我们的系统动态生成的任何端口(heroku生成动态端口)。
If you find any of the above confusing, you can take an introductory course on Node.js
如果您发现以上任何令人困惑的地方,都可以参加Node.js 入门课程
Then, create another file config.js
in the same directory and add the following to it:
然后,在同一目录中创建另一个文件config.js
并将以下内容添加到其中:
module.exports = {
'secret': 'supersecret'
};
Finally, create another file db.js
and add the following to it:
最后,创建另一个文件db.js
并添加以下内容:
"use strict";
const sqlite3 = require('sqlite3').verbose();
class Db {
constructor(file) {
this.db = new sqlite3.Database(file);
this.createTable()
}
createTable() {
const sql = `
CREATE TABLE IF NOT EXISTS user (
id integer PRIMARY KEY,
name text,
email text UNIQUE,
user_pass text,
is_admin integer)`
return this.db.run(sql);
}
selectByEmail(email, callback) {
return this.db.get(
`SELECT * FROM user WHERE email = ?`,
[email],function(err,row){
callback(err,row)
})
}
insertAdmin(user, callback) {
return this.db.run(
'INSERT INTO user (name,email,user_pass,is_admin) VALUES (?,?,?,?)',
user, (err) => {
callback(err)
})
}
selectAll(callback) {
return this.db.all(`SELECT * FROM user`, function(err,rows){
callback(err,rows)
})
}
insert(user, callback) {
return this.db.run(
'INSERT INTO user (name,email,user_pass) VALUES (?,?,?)',
user, (err) => {
callback(err)
})
}
}
module.exports = Db
We created a class for our database to abstract the basic functions we need. You may want to use more generic and reusable methods here for database operations and likely use a promise to make it more efficient. This will allow you have a repository you can use with all other classes you define (especially if your application uses MVC architecture and has controllers).
我们为数据库创建了一个类来抽象我们所需的基本功能。 您可能希望在此处对数据库操作使用更通用和可重用的方法,并可能使用Promise提高效率。 这将使您拥有一个可与定义的所有其他类一起使用的存储库(尤其是如果您的应用程序使用MVC架构并且具有控制器)。
All done and looking good, let’s make the vue application now.
一切都完成了,看起来不错,让我们现在制作vue应用程序。
The vue-router file can be found in ./src/router/
directory. In the index.js
file, we will define all the routes we want our application to have. This is different from what we did with our server and should not be confused.
可以在./src/router/
目录中找到vue-router文件。 在index.js
文件中,我们将定义我们希望应用程序具有的所有路由。 这与我们对服务器所做的操作不同,因此请勿混淆。
Open the file and add the following:
打开文件并添加以下内容:
import Vue from 'vue'
import Router from 'vue-router'
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'
Vue.use(Router)
We have imported all the components our application will use. We will create the components below.
我们已经导入了应用程序将使用的所有组件。 我们将在下面创建组件。
Now, let's define the routes for our application:
现在,让我们为应用程序定义路由:
let router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
guest: true
}
},
{
path: '/register',
name: 'register',
component: Register,
meta: {
guest: true
}
},
{
path: '/dashboard',
name: 'userboard',
component: UserBoard,
meta: {
requiresAuth: true
}
},
{
path: '/admin',
name: 'admin',
component: Admin,
meta: {
requiresAuth: true,
is_admin : true
}
},
]
})
Vue router allows use define a meta on our routes so we can specify additional behaviour. In our case above, we have defined some routes as guest (which means only users not authenticated should see it), some to require authentication (which means only authenticated users should see it) and the last one to be only accessible to admin users.
Vue路由器允许使用在我们的路由上定义一个meta,因此我们可以指定其他行为。 在上面的示例中,我们将某些路由定义为访客(这意味着只有未认证的用户才能看到它),一些路由需要进行认证(这意味着只有已认证的用户应该看到它),而最后一条仅对管理员用户可用。
Now, let's handle requests to these routes based on the meta specification:
现在,让我们根据meta规范处理对这些路由的请求:
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.requiresAuth)) {
if (localStorage.getItem('jwt') == null) {
next({
path: '/login',
params: {
nextUrl: to.fullPath }
})
} else {
let user = JSON.parse(localStorage.getItem('user'))
if(to.matched.some(record => record.meta.is_admin)) {
if(user.is_admin == 1){
next()
}
else{
next({
name: 'userboard'})
}
}else {
next()
}
}
} else if(to.matched.some(record => record.meta.guest)) {
if(localStorage.getItem('jwt') == null){
next()
}
else{
next({
name: 'userboard'})
}
}else {
next()
}
})
export default router
Vue-router has a beforeEach method that is called before each route is processed. This is where we can define our checking condition and restrict user access. The method takes three parameters — to, from and next. to
is where the user wishes to go, from
is where the user is coming from, next
is a callback function that continues the processing of the user request. Our check is on the to
object.
Vue-router有一个beforeEach方法,该方法在处理每个路由之前被调用。 在这里我们可以定义检查条件并限制用户访问权限。 该方法采用三个参数-to,from和next。 to
是用户希望去的地方, from
是用户来自的地方, next
是继续处理用户请求的回调函数。 我们的检查对象是to
。
We check a few things:
我们检查一下几件事:
requiresAuth
, check for a jwt
token showing the user is logged in. 如果路线requiresAuth
,检查用于jwt
令牌示出用户登录。 requiresAuth
and is only for admin users, check for auth and check if the user is an admin 如果route requiresAuth
且仅适用于管理员用户,请检查auth并检查该用户是否为管理员 guest
, check if the user is logged in 如果路线要求guest
,请检查用户是否已登录 We redirect the user based on what we are checking for. We use the name of the route to redirect, so check to be sure you are using this for your application.
我们根据要检查的内容重定向用户。 我们使用路由的名称进行重定向,因此请检查以确保将其用于您的应用程序。
IMPORTANT: Always ensure you have
next()
called at the end of every condition you are checking. This is to prevent your application from failing in the event that there is a condition you forgot to check.重要说明:始终确保在检查的每个条件的末尾都有
next()
调用。 这是为了防止您的应用程序在您忘记检查的情况下失败。
To test out what we have built, let’s define a few components. In the ./src/components/
directory, open the HelloWorld.vue
file and add the following:
为了测试我们构建的内容,让我们定义一些组件。 在./src/components/
目录中,打开HelloWorld.vue
文件并添加以下内容:
<template>
<div class="hello">
<h1>This is homepage</h1>
<h2>{
{
msg}}</h2>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello World!'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
Create a new file Login.vue
in the same directory and add the following:
在同一目录中创建一个新文件Login.vue
并添加以下内容:
<template>
<div>
<h4>Login</h4>
<form>
<label for="email" >E-Mail Address</label>
<div>
<input id="email" type="email" v-model="email" required autofocus>
</div>
<div>
<label for="password" >Password</label>
<div>
<input id="password" type="password" v-model="password" required>
</div>
</div>
<div>
<button type="submit" @click="handleSubmit">
Login
</button>
</div>
</form>
</div>
</template>
That is for the HTML template. Now, let's define the script handling login:
那是HTML模板。 现在,让我们定义处理登录的脚本:
<script>
export default {
data(){
return {
email : "",
password : ""
}
},
methods : {
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);
});
}
}
}
}
</script>
At this point, we have the email
and password
data attributes bound to the form fields to collect user input. We made a request to the server to authenticate the credentials the user supplies.
至此,我们已将email
和password
数据属性绑定到表单字段以收集用户输入。 我们向服务器发出了请求,要求对用户提供的凭据进行身份验证。
Now, let's use the response from the server:
现在,让我们使用服务器的响应:
[...]
methods : {
handleSubmit(e){
[...]
.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')
}
}
}
})
[...]
}
}
}
}
We store the jwt
token and user
information in localStorage so we can access it from all parts of our application. Of course, we redirect the user to whichever part of our application they tried to access before being redirected to login. If they came to login directory, we redirect them based on the user type.
我们将jwt
令牌和user
信息存储在localStorage中,因此我们可以从应用程序的所有部分对其进行访问。 当然,我们将用户重定向到他们尝试访问的应用程序的任何部分,然后再重定向至登录。 如果它们进入登录目录,我们将根据用户类型重定向它们。
If you are not familiar with composing vue components, you can take this Getting Started With Vue course.
如果您不熟悉组成Vue组件,则可以参加本“ Vue入门”课程。
Next, create a Register.vue
file and add the following to it:
接下来,创建一个Register.vue
文件,并添加以下内容:
<template>
<div>
<h4>Register</h4>
<form>
<label for="name">Name</label>
<div>
<input id="name" type="text" v-model="name" required autofocus>
</div>
<label for="email" >E-Mail Address</label>
<div>
<input id="email" type="email" v-model="email" required>
</div>
<label for="password">Password</label>
<div>
<input id="password" type="password" v-model="password" required>
</div>
<label for="password-confirm">Confirm Password</label>
<div>
<input id="password-confirm" type="password" v-model="password_confirmation" required>
</div>
<label for="password-confirm">Is this an administrator account?</label>
<div>
<select v-model="is_admin">
<option value=1>Yes</option>
<option value=0>No</option>
</select>
</div>
<div>
<button type="submit" @click="handleSubmit">
Register
</button>
</div>
</form>
</div>
</template>
Now, define the script handling registration:
现在,定义脚本处理注册:
<script>
export default {
props : ["nextUrl"],
data(){
return {
name : "",
email : "",
password : "",
password_confirmation : "",
is_admin : null
}
},
methods : {
handleSubmit(e) {
e.preventDefault()
if (this.password === this.password_confirmation && this.password.length > 0)
{
let url = "http://localhost:3000/register"
if(this.is_admin != null || this.is_admin == 1) url = "http://localhost:3000/register-admin"
this.$http.post(url, {
name: this.name,
email: this.email,
password: this.password,
is_admin: this.is_admin
})
.then(response => {
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{
this.$router.push('/')
}
}
})
.catch(error => {
console.error(error);
});
} else {
this.password = ""
this.passwordConfirm = ""
return alert("Passwords do not match")
}
}
}
}
</script>
This is similar in structure to the Login.vue
file. It creates the register component and accompanying method to handle user submission of the registration form.
这在结构上与Login.vue
文件类似。 它创建了注册组件和随附的方法来处理用户提交的注册表单。
Now, create the file Admin.vue
and add the following:
现在,创建文件Admin.vue
并添加以下内容:
<template>
<div class="hello">
<h1>Welcome to administrator page</h1>
<h2>{
{
msg}}</h2>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'The superheros'
}
}
}
</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;
}
</style>
This is the component we will mount when a user visits the admin page.
这是当用户访问管理页面时我们将安装的组件。
Finally, create the file UserBoard.vue
and add the following:
最后,创建文件UserBoard.vue
并添加以下内容:
<template>
<div class="hello">
<h1>Welcome to regular users page</h1>
<h2>{
{
msg}}</h2>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'The commoners'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
This is the file we will see when a user visits the dashboard page.
这是用户访问仪表板页面时将看到的文件。
And that’s it for components.
这就是组件。
For all our server requests, we will use axios. Axios is a promise based HTTP client for the browser and node.js. Run the following command to install axios:
对于所有服务器请求,我们将使用axios 。 Axios是用于浏览器和node.js的基于Promise的HTTP客户端。 运行以下命令以安装axios:
$npm install --save axios
To make it accessible across all our components, open the ./src/main.js
file and add the following:
要使其在我们所有组件中均可访问,请打开./src/main.js
文件并添加以下内容:
import Vue from 'vue'
import App from './App'
import router from './router'
import Axios from 'axios'
Vue.prototype.$http = Axios;
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: {
App },
template: ' '
})
By defining Vue.prototype.$http = Axios
we have modified the vue engine and added axios. We can now use axios in all our components like this.$http
.
通过定义Vue.prototype.$http = Axios
我们修改了vue引擎并添加了axios。 现在,我们可以在this.$http
这样的所有组件中使用axios。
Now that we are done with the application, we need to build all our assets and run it. Because we have a node js server along with our vue application, we will be both of them for our application to work.
现在我们已经完成了应用程序的构建,我们需要构建所有资产并运行它。 因为我们有一个节点js服务器以及我们的vue应用程序,所以对于我们的应用程序来说,我们将同时成为这两者。
Let’s add a script that will help us run our node server. Open the package.json
file and add the following:
让我们添加一个脚本,以帮助我们运行节点服务器。 打开package.json
文件并添加以下内容:
[...]
"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"
},
[...]
We added the server
script to help us start up the node server. Now, run the following command to start the server:
我们添加了server
脚本来帮助我们启动节点服务器。 现在,运行以下命令来启动服务器:
$npm run server
You should see something like this:
您应该会看到以下内容:
Then create another terminal instance and run the vue app like this:
然后创建另一个终端实例并按如下方式运行vue应用程序:
$npm run dev
This will build all the assets and st art the application. You can open the link it shows you to see the application
这将构建所有资产并优化应用程序。 您可以打开显示您看到应用程序的链接
In this guide, we have seen how to use vue-router to define checks on our routes and prevent users from accessing certain routes. We also saw how to redirect users to different parts of our application based on the authentication state. We also built a mini server with Node.js to handle user authentication.
在本指南中,我们了解了如何使用vue-router定义对路由的检查并防止用户访问某些路由。 我们还看到了如何根据身份验证状态将用户重定向到应用程序的不同部分。 我们还使用Node.js构建了一个微型服务器来处理用户身份验证。
What we did is an example of how access control is designed in frameworks like Laravel. You can checkout out vue-router and see what other cool things you can do with it.
我们所做的是一个示例,说明如何在Laravel之类的框架中设计访问控制。 您可以签出vue-router并查看您可以使用它进行哪些其他有趣的操作。
翻译自: https://scotch.io/tutorials/vue-authentication-and-route-handling-using-vue-router
vue 使用路由器