• 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、运行效果

     

  • 相关阅读:
    Spring Boot (20) 拦截器
    Spring Boot (19) servlet、filter、listener
    Spring Boot (18) @Async异步
    Spring Boot (17) 发送邮件
    Spring Boot (16) logback和access日志
    Spring Boot (15) pom.xml设置
    Spring Boot (14) 数据源配置原理
    Spring Boot (13) druid监控
    Spring boot (12) tomcat jdbc连接池
    Spring Boot (11) mybatis 关联映射
  • 原文地址:https://www.cnblogs.com/lone5wolf/p/14865572.html
Copyright © 2020-2023  润新知