赞
踩
Vue.js is a progressive JavaScript framework for building front-end applications. Coupled with vue-router, we can build high performance applications with complete dynamic routes. Vue-router is an efficient tool and can efficiently handle authentication in our Vue application.
Vue.js是用于构建前端应用程序的渐进式JavaScript框架。 结合vue-router ,我们可以构建具有完整动态路由的高性能应用程序。 Vue路由器是一种高效的工具,可以在我们的Vue应用程序中有效地处理身份验证。
In this tutorial, we will look at using vue-router to handle authentication and access control for different parts of our Vue.js application.
在本教程中,我们将研究如何使用vue-router处理Vue.js应用程序不同部分的身份验证和访问控制。
To begin, install the Vue CLI and create a Vue application with it:
首先,安装Vue CLI并使用它创建一个Vue应用程序:
Follow the setup prompt and complete the installation of this application. If you are not sure of an option, 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.
按照安装提示完成此应用程序的安装。 如果不确定选项,请单击返回键( ENTER
键)以继续使用默认选项。 当要求安装vue-router时,请接受该选项,因为我们需要此应用程序的vue-router。
Next, we will set up a Node.js server that will handle authentication for us. For our Node.js server, we will use SQLite as the database of choice.
接下来,我们将设置一个Node.js服务器来为我们处理身份验证。 对于我们的Node.js服务器,我们将使用SQLite作为选择的数据库。
Run the following command to install SQLite driver:
运行以下命令以安装SQLite驱动程序:
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哈希所有密码。 运行以下命令进行安装:
We also want a way to confirm the users 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软件包:
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 Node.js 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.
至此,我们创建了一个可以处理用户身份验证的Node.js服务器。 创建一个名为server
的新目录。 在这里,我们将存储用于制作节点后端的所有内容。
In the server directory, create a file and save it as app.js
. Add the following to it:
在服务器目录中,创建一个文件并将其另存为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, and created an Express server and router.
我们需要应用程序所需的所有软件包,定义了数据库,并创建了Express服务器和路由器。
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 to handle the response from the database operation. As expected, we have defined error checks to ensure we provide accurate information to 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, feel free to respond to the user how you please.
对于登录,我们使用bcrypt
将哈希密码与用户提供的密码进行比较。 如果它们相同,我们将使用户登录。如果不相同,请随时以您满意的方式答复用户。
Now, let’s use the Express server to make our application accessible:
现在,让我们使用Express服务器来使我们的应用程序可访问:
- 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.
我们在port: 3000
或系统上任何动态生成的端口上创建了服务器。
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 to have a repository you can use with all other classes you define (especially if your application uses MVC architecture and has controllers).
我们为数据库创建了一个类,以抽象出所需的基本功能。 您可能希望在此处对数据库操作使用更通用和可重用的方法,并可能使用Promise提高效率。 这将使您拥有一个可与定义的所有其他类一起使用的存储库(尤其是如果您的应用程序使用MVC架构并具有控制器)。
The vue-router file can be found in the ./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 later.
我们已经导入了应用程序将使用的所有组件。 稍后我们将创建组件。
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 us to define a meta on our routes so we can specify additional behavior. In our case above, we have defined some routes as guest (which means only users not authenticated will see it), some to require authentication (which means only authenticated users will see it), and the last one to be only accessible to admin users.
Vue路由器允许我们在路线上定义一个元,因此我们可以指定其他行为。 在上面的示例中,我们将某些路由定义为访客(这意味着只有未认证的用户才能看到它),有些需要进行认证(这意味着只有经过认证的用户才可以看到它),最后一条仅对管理员用户可以访问。 。
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, and 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:
我们检查一下几件事:
if route requiresAuth
, check for a jwt
token showing the user is logged in.
如果路线requiresAuth
,检查用于jwt
令牌示出用户登录。
if route requiresAuth
and is only for admin users, check for auth and check if the user is an admin
如果route requiresAuth
且仅适用于管理员用户,请检查auth并检查该用户是否为管理员
if route requires 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.
我们根据要检查的内容重定向用户。 我们使用路由的名称进行重定向,因此请检查以确保将其用于您的应用程序。
Warning: 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. We redirect the user to whichever part of our application they tried to access before being redirected to login. If they came to the login directory, we redirect them based on the user type.
我们将jwt
令牌和user
信息存储在localStorage
以便可以从应用程序的所有部分访问它。 我们将用户重定向到他们尝试访问的应用程序的任何部分,然后再重定向至登录。 如果它们进入登录目录,我们将根据用户类型重定向它们。
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.
这是用户访问仪表板页面时将看到的文件。
For all our server requests, we will use axios. Axios is a promise based HTTP client for the browser and Node.js.
对于所有服务器请求,我们将使用axios 。 Axios是用于浏览器和Node.js的基于Promise的HTTP客户端。
Run the following command to install axios:
运行以下命令以安装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: '<App/>'
- })
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 need both of them for our application to work.
现在我们已经完成了应用程序的构建,我们需要构建所有资产并运行它。 因为我们有一个Node.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
脚本来帮助我们启动节点服务器。 现在,运行以下命令来启动服务器:
Then create another terminal instance and run the Vue app like this:
然后创建另一个终端实例并按如下方式运行Vue应用程序:
This will build all the assets and start the application. You can open the link it shows you to see the application.
这将构建所有资产并启动应用程序。 您可以打开显示您看到该应用程序的链接。
In this guide, we have used 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. Finally, we 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 check out out vue-router and see what else you can do with it.
我们所做的是一个示例,说明如何在Laravel之类的框架中设计访问控制。 您可以签出vue-router并查看它还能做什么。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。