token + localstorage 验证登录(vue)详细教程

🚩概要

  • token :
    本质是验证身份的令牌,一般由用户通过账户密码登录后,服务端把这些凭证通过加密等一些列操作后得到的字符串。
  • token 登录流程:
    • 客户端用账户密码请求登录;
    • 服务端接收请求,验证账户密码;
    • 验证成功后,服务端发送token给客户端;
    • 客户端接收到token后保存,可以存储于sessionstorage或localstorage;
    • 客户端每次请求,需要将携带token发送给服务端;
    • 服务端接收请求,验证token,验证成功则响应请求返回请求数据;
  • token 验证优点:
    • token 存储于客户端,不会对服务端造成压力,即便是服务端集群,也不会增加维护成本;
    • token 在前端可以不保存于 Cookie 中,避免CSRF攻击,提升数据的安全性;
    • token 下发后,在有效期内长久生效,服务端想回收 token 权限非常困难;

✌️步骤:

1.demo构建

  • 前端:采用 token + localStorage + vuex 做令牌存储验证,包含 axios 二次封装、路由守卫、api 模块化等操作;
  • 后端:采用👉 mock.js 请求模拟,模拟登录接口测试数据获取等 ;
    通过vueCLI脚手架搭建,包含以下基本结构:

项目结构

2.后端Mock

mock 文件夹下新建 index.js,编写登录与数据获取测试接口以便后面模拟请求调取,注意需要在 main.js 引入

const Mock = require('mockjs');

// 登录接口
Mock.mock('/login', 'post', (option) => {
  // 解析请求体 body 中数据
  let response = JSON.parse(option.body);
  return {
    status: 200,
    message: '请求成功',
    data: response,
    token: 'authorization-xxxxxx' // 模拟后端返回 token
  }
})

// 数据请求模拟
Mock.mock('/data','get',(option)=> {
  return {
    status: 200,
    message: "请求成功",
    data: [1,2,3,4,5]
  }
})

main.js
在这里插入图片描述

3、login.vue

<template>
  <div>
    <input type="text" v-model="form.username" placeholder="用户名">
    <input type="password" v-model="form.password" placeholder="密码">
    <button @click="login">登录</button>
  </div>
</template>
<script>
import { mapMutations } from 'vuex'
import { Login } from '../api/api.js'
export default {
  name: 'Login',
  data() {
    return {
      form: {
        username: '',
        password: ''
      },
      token: ''
    }
  },
  methods: {
    ...mapMutations(['setToken']),

    login() {
      if(this.form.username === '' || this.form.password === '') {
        alert('账号密码不能为空');
      }else {
        let form = new FormData();
        form.append('username', this.form.username);
        // 密码一般要加密处理,根据项目需求而定
        form.append('password', this.form.password);
        Login(form).then(res => {
          console.log(res);
          this.token = 'Bearer' + res.data.token;
          this.setToken({token: this.token});
          this.$router.push('/home');
          alert('登录成功');
        }).catch( err =>{
          alert('账号密码错误');
          console.log(err)
        })      
      }
    }
  }
}
</script>
</script>

4、home.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="getData">获取数据</button>
    <button @click="clearToken">清除token</button>
  </div>
</template>
<script>
import { getData } from '@/api/api';
export default {
  name: "Home",
  data() {
    return {
      msg:'数据等待获取'
    }
  },
  methods: {
    getData() {
      getData().then(res => {
        console.log(res)
        this.msg = res.data.data
      })
    },
    clearToken() {
      localStorage.removeItem('token')
    }
  }


};
</script>

5、路由router

添加前置路由守卫,访问其他页面需携带 token 并进行验证

import Vue from "vue";
import VueRouter from "vue-router";

const home = () => import("@/components/Home.vue");
const login = () => import("@/components/Login.vue");

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    redirect: "/login"
  },
  {
    path: "/login",
    name: "login",
    component: login
  },
  {
    path: "/home",
    name: "home",
    component: home
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

/**
*	重写 router.prototype.push 避免同时跳转相同路径报错
*/
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch(err => err)
}

// 前置路由守卫
router.beforeEach( (to, from, next) => {
  if(to.path === '/login') {
    next();
  }else {
    let token = localStorage.getItem('token');
    if(token === null || token === '')  {
      next('/login')
    }else {
      next()
    }
  }
})
export default router;

6、vuex存储

token 登录获取到后,一般要本地 localStorage 和 vuex 中存一份, 存 vuex 目的是用于需要授权接口调用,存入 vuex 便于统一管理;localStorage 数据是存放在硬盘中, vuex 数据是存储内存中读取速度更快。

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    // 存储token
    token: localStorage.getItem("token") ? localStorage.getItem("token") : ""
  },
  mutations: {
    // 修改token ,并将token 存入localStorage
    setToken (state, user) {
      state.token = user.token;
      localStorage.setItem("token",user.token);
    }
  },
});

7、axios 二次封装

一般大型项目都需要对 axios 二次封装,以便请求统一处理与接口模块化管理

import router from "@/router";
import axios from "axios"
import qs from "qs"

// 配置开发&生产环境接口,根据 node 环境变量来进行判断
const devBaseURL = '/';
const proBaseURL = 'http://prod.xxxx';
const baseURL = process.env.NODE_ENV === "development" ? devBaseURL : proBaseURL;

const instance = axios.create({
  baseURL: baseURL,
  timeout: 5000, // 设置超时时间
  withCredentials: true // 设置是否允许跨域传递的 cookie 携带凭证
})

// 配置请求参数传递格式,默认是JSON格式,根据服务器决定
instance.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
instance.defaults.transformRequest = data => qs.stringify(data)

// 配置 axios 请求拦截器, 配置 token 登录认证
instance.interceptors.request.use(
  config => {
    // 配置每次发送请求之前判断是否存在 token,存在则在请求头 header 上添加 token,
    let token = localStorage.getItem('token');
    token && (config.headers.Authorization = token);
    return config;
  },
  error => {
    return Promise.reject(error)
  }
)

// 配置 axios 响应拦截器
instance.interceptors.response.use(
  // 判断返回状态码,进行对应的数据返回与异常抛出操作
  response => {
    if(response.status === 200) {
      return Promise.resolve(response)
    }else {
      return Promise.reject(response)
    }
  },
  // 配置服务器状态码不是200的情况
  error => {
    if(error.response) {
      switch(error.response.status) {
        // 401: 未登录
        case 401: 
          localStorage.removeItem('token');
          alert('连接超时,请重新登录');
          router.push({path: '/'});
          break
        // 403: 登录过期
        case 403:
          alert('登录过期,请重新登录');
          localStorage.removeItem('token');
          router.push({path: '/'});
          break
        // 404: 请求不存在
        case 404: 
          alert('网络请求不存在');
          break
        // 500: 服务错误
        case 500:
          alert('网络请求有误');
        default:
          alert('服务错误');
      }
      //return Promise.reject('服务错误')
    }else {
      // 配置服务器没有返回结果情况
      if(!window.navigator.onLine) {
        // 断网情况,进行断网处理
        return
      }
      return Promise.reject(error)
    }
  }
)

export default instance

8、api.js

对接口模块化处理,方便管理调用

import request from '../utlls/request'

export function Login(params){
  return request ({
    method: 'post',
    url: '/login',
    data: params
  })
}

export function getData(params) {
  return request({
    method: 'get',
    url: '/data',
    params
  })
}

✔️测试

  • 第一登录后,打印请求结果,可看到模拟请求返回数据中 token 字段
    token获取
  • 登录后,点击‘获取数据’按钮,打印请求结果,可看到请求头 Authorization 已包含 token
    在这里插入图片描述
  • 点击‘清除token’按钮,刷新页面则会自动退回登录界面

至此登录 demo 完成,若有问题欢迎指正,编写不易还望三连!😘😘😘

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值