zoukankan      html  css  js  c++  java
  • Vue项目中实现用户登录及token验证

    一、什么是token

          token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。当用户第一次登录后,服务器生成一个token并将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。

    简单token的组成;uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token泄露)。

    二、token的作用

      1、防止表单重复提交:主要的理念是,客户端初始化的时候(一般就是刚刚进入页面的时候)就调用后端代码,后端代码生成一个token,返回给客户端,客户端储存token(可以在前台使用Form表单中使用隐藏域来存储这个Token,也可以使用cookie),然后就将request(请求)中的token与(session)中的token进行比较

       2、用来作身份验证:

    (1)身份认证概述

       由于HTTP是一种没有状态的协议,它并不知道是谁访问了我们的应用。这里把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下次这个客户端再发送请求时候,还得再验证一下。

      通用的解决方法是:当用户请求登录的时候,如果没有问题,在服务端生成一条记录,在这个记录里可以说明登录的用户是谁,然后把这条记录的id发送给客户端,客户端收到以后把这个id存储在cookie里,下次该用户再次向服务端发送请求的时候,可以带上这个cookie,这样服务端会验证一下cookie里的信息,看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。

    以上所描述的过程就是利用session,那个id值就是sessionid。我们需要在服务端存储为用户生成的session,这些session会存储在内存,磁盘,或者数据库。

    2)基于token机制的身份认证:使用token机制的身份验证方法,在服务器端不需要存储用户的登录记录。流程如下:

    1. 客户端使用用户名和密码请求登录。
    2. 服务端收到请求,验证用户名和密码。
    3. 验证成功后,服务端会生成一个token,然后把这个token发送给客户端。
    4. 客户端收到token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里。
    5. 客户端每次向服务端发送请求的时候都需要带上服务端发给的token。
    6. 服务端收到请求,然后去验证客户端请求里面带着token,如果验证成功,就向客户端返回请求的数据。

    三、Vue项目中实现用户登录及token验证

       1、思路如下:

    (1) 第一次登录的时候,前端调后端的登陆接口,发送用户名和密码

    (2) 后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token

    (3) 前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面

    (4) 前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面

    (5) 每次调后端接口,都要在请求头中加token

    (6) 后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401

    (7) 如果前端拿到状态码为401,就清除token信息并跳转到登录页面

       2、后台接口(node.js)的实现

         1)安装JWT:  npm install jsonwebtoken --save

    JWT(Json Web Token)是一种身份验证及授权方案,简单的说就是客户端调用 api 时,附带上一个由 api 端颁发的 token,以此来验证调用者的授权信息。

          (2)JWT的常用函数:

             Asign(payload, secretOrPrivateKey, [options, callback])

     

           Bverify(token, secretOrPublicKey, [options, callback])

     

          Cdecode(token [, options])

     

    (3)后台代码实现:

    var express = require('express');

    var router = express.Router();

    var pool = require('../config/blogdb.js')

    const jwt = require('jsonwebtoken'); //引入jwt模块

    /**

     * http://localhost:8089/blog/login

     */

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

        let username = req.body.username;

        let password = req.body.password;

        console.log("用户名="+username)

        pool.getConnection(function(err,conn){

          if(err){

              console.log('连接数据库失败!')

          }else{

              let data=[username,password]

              let sql = "select * from admin where username= ? and password = ?";

              conn.query(sql,data,function(error,results){

                  if(error){

                      console.log(error)

                  }

                 if(results != null){ //若查询结果不为空

                    const payload = { //定义token的有限载荷

                        name: results.username

                       }

                       const secret = 'deyun' //给定密钥

                       //定义token

                    const token = jwt.sign(payload,secret,{ 

                        'expiresIn':1440});// 设置过期时间

                 

                    res.json({ //将响应信息转换为json格式

                        success: true,

                        message: 'Enjoy your token',

                        token: token

                    })

                }

                  conn.release(); //释放数据库连接对象

              })

          }

        });

      })

      module.exports = router;

     

    (4)JWT机制优缺点

           优点:

    • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
    • 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
    • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
    • 去耦:不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
    • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
    • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
    • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
    • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)
    • 缺点:
    • JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
    • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令×××的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
    • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

    三、前端Vue实现

       1、创建项目:vuetokendemo,目录结构如下

    2、给项目安装vuex模块

    3、main.js文件

    import Vue from 'vue'

    import App from './App'

    import router from './router'

    import axios from 'axios' 

    import store from './store' //导入store

    Vue.config.productionTip = false

    Vue.prototype.$http = axios 

    // 导航守卫

    // 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆

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

      if(to.path === '/login'){

        next();

      }else{

        let token = window.localStorage.token;

        if(token === 'null' || token === '' || token === undefined){

          next('/login')

        }else{

          next();

        }

      }

    });

    //添加请求拦截器

    axios.interceptors.request.use(

      config => {

        if(store.state.token){

          config.headers.common['token'] = store.state.token.token

        }

        return config;

      },

      error => {

        //请求错误

        return Promise.reject(error);

      }

    );

    //添加响应拦截器

    axios.interceptors.response.use(

      response => {

        return response;

      },

      error => {

        if(error.response){

          switch(error.response.status){

            case 401:

              localStorage.removeItem('token');

              router.replace({

                path: '/login',

                query: {

                  redirect: router.currentRoute.fullPath //登录成功后跳入浏览的当前页

                }

              })

          }

        }

      }

    )

    new Vue({

      el: '#app',

      router,

      store,

      components: { App },

      template: '<App/>'

    })

     

     4、login.vue组件

    <template>

      <div>

        <input type="text" v-model="loginForm.username" placeholder="用户名"/>

        <input type="text" v-model="loginForm.password" placeholder="密码"/>

        <button @click="login">登录</button>

      </div>

    </template>

    <script>

    import { mapMutations } from 'vuex'

    export default {

        data () {

            return {

            loginForm: {

                username: '',

                password: ''

            }

            };

        },

        methods: {

            ...mapMutations(['$_setStorage']),

            login () {

              let _this = this;

              if(this.loginForm.username === '' || this.loginForm.password === ''){

                    alert('账号或密码不能为空');

                }else{

                    this.$http({

                        method: 'post',

                        url: 'http://localhost:8089/blog/login',

                        data: _this.loginForm

                    }).then(res=>{

                        console.log(res.data)

                        _this.$_setStorage({Authorization:res.data.token});

                        _this.$router.push('/home');

                        alert('登录成功')

                    }).catch(error=>{

                        alert('账号或密码错误');

                        console.log(error)

                    })

                }

            }

        }

    }

    </script>

     

    5、home.vue组件

    <template>

       <h1>首页,跳转成功</h1>

    </template>

    <script>

    export default {

    }

    </script>

    <style>

    </style>

    6、store/index.js文件

    import Vue from 'vue'

    import Vuex from 'vuex'

    Vue.use(Vuex)

    const key = 'token'

    const store = new Vuex.Store({

        state () {

            return {

              token: localStorage.getItem('token')?localStorage.getItem('token'):''

            }

        },

        getters: {

            getSortage: function(state) {

                if(!state.token){

                    state.token =JSON.parse(localStorage.getItem(key))

                }

                return state.token

            }

        },

        mutations: {

            $_setStorage(state,value){

                state.token = value

                localStorage.setItem(key,JSON.stringify(value))

            },

            $_removeStorage(state){

                state.token = null;

                localStorage.removeItem(key)

            }

        }

    })

    export default store;

    7、router/index.js文件

    import Vue from 'vue'

    import Router from 'vue-router'

    import login from '@/components/login'

    import home from '@/components/home'

    Vue.use(Router)

    const router = new Router({

      routes: [

        {

          path: '/', 

          redirect: '/login'

        },

        {

          path: '/login',

          name: 'login',

          component: login

        },

        {

          path: '/home',

          name: 'home',

          component: home

        }

      ]

    });

    //导航守卫

    //使用router.beforeEach注册一个全局前置守卫,判断用户是否登录

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

      if(to.path === '/login'){

        next();

      }else{

        let token = localStorage.getItem('Authorization');

        if( token === 'null' || token === ''){

          next('/login')

        }else{

          next();

        }

      }

    });

    export default router;

    8、运行效果

     

  • 相关阅读:
    Java中List集合去除重复数据的六种方法
    常见的Redis面试"刁难"问题,值得一读
    以Integer类型传参值不变来理解Java值传参
    Linux系统安装snmp服务
    直接取数据到RANGE
    SAP翔子_2019集结号
    销售订单BOM组件分配(CP_BD_DIRECT_INPUT_PLAN_EXT)
    SAP翔子_webservice篇索引
    函数篇3 EXCEL导入函数去除行数限制
    ABAP基础篇4 常用的字符串操作语法
  • 原文地址:https://www.cnblogs.com/lone5wolf/p/14865572.html
Copyright © 2011-2022 走看看