浅谈登录和退出

登录和退出

  • 需求:
    • 1:点击登录的时候,跳转到首页
    • 2:假如用户点击的是 用户列表 那么返回到用户列表 => 则需要使用全局路由守卫 记录这个path
    • 3:点击退出的时候,返回到登录界面,清空token!

登录

  • 需求:
    • 点击登录的时候,先验证表单
    • 把当前的用户名,存储在本地存储之中 (用户名 密码 验证码 token)
    • 并且把头部的登录隐藏 显示退出按钮!

router/index.js路由的配置

  • 登录组件 和 home组件 同级!

    • 在组件内最外围添加一个meta:{ } 里面设置 证书授权 requiresAuth: true title:“标题”
    • 全局前置路由守卫判断 是否含有 证书授权 => 有的进行里面判断 没有的话,就直接next()
      if(to.matched.some(item => item.meta.requiresAuth){}else{ next() }
    • 先从本地存储拿到存储的用户登录信息!(userInfo) => 字符串的形式 (还需要一个 || {})
      • 需要把字符串转化为对象形式,方便拿出token值 ,使用try{ 里面转化为对象} catch(err){ userInfo = {}}
      • 然后从userInfo对象中判断是否有这个token值(authorization)
        • token值存在的话,那么就是进行判断,否则直接next(到登录,还记录当前用户点击的path)
          if(userInfo.authorization){
            //进行判断
          }else{
            next({
              path:"/login",
              query:{
                redirectTo:to.fullpath //去往的那个path完整路径
              }
            })
          }
        
        • token值存在时的判断!
          • 发起请求,这就需要先引入发起请求的文件了! import $request from "request"
          • 先给next(),但是发起请求,判断token值是否失效了!,如果请求回来的token值还是成功的状态,那么就直接next(),否则的话,还是依旧 path=> 去往login,并且记录当前用户所想要去的完整path
          $request.get("/jwtverify",{
            params:{
              authorization:userInfo.authorization
            }
          })
          .then(data => {
            if(data.data.code === 1){
              next()
            }else{
              next({
                path:"/login",
                query:{
                  redirectTo:to.fullPath
                }
              })
            }
          })
          next()
        
  • 完整代码!

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

import Login0 from "../pages/Login0.vue"
import Reg from "../pages/Reg.vue"
import NotFound from "../pages/NotFound.vue"
import $request from "../netword/request"

Vue.use(VueRouter)
const router = new VueRouter({
  routes: [{
      path: "/",
      redirect: "/login",
    },{
      path: '/404',
      component: NotFound
    }, // 404页面效果
    {
      path: '*',
      redirect: '/404'
    }, {
      path: "/login",
      name: "Login",
      component: () => import("../pages/Login.vue")
    }, {
      path: "/home",
      component: () => import("../pages/home/Home.vue"),
      children: [{
          path: "/user",
          meta: {
            title: "用户",
            requiresAuth: true
          },
          component: () => import("../pages/user/Default.vue"),
          children: [{
              path: "/",
              redirect: "list"
            }, {
              path: "list",
              name: "UserList",
              meta: {
                title: "用户列表"
              },
              component: () => import("../pages/user/List.vue")
            },
            {
              path: "add",
              name: "UserDdd",
              // component: () => import("../pages/user/Add.vue")
              meta: {
                title: "用户添加"
              },
              component: () => import("../pages/user/Edit.vue")
            }, {
              name: "UserEdit",
              // path: "edit",
              path: "edit/:id",
              meta: {
                title: "用户编辑"
              },
              component: () => import("../pages/user/Edit.vue")
            }
          ]
        }, {
          name: "Order",
          path: "/order",
          meta: {
            title: "订单管理",
            requiresAuth: true
          },
          component: () => import("../pages/order/OrderHome.vue"),
          children: [{
            path: "/",
            redirect: "list"
          }, {
            path: "list",
            name: "OrderList",
            meta: {
              title: "订单分类"
            },
            component: () => import("../pages/order/OdrderList.vue")
          }, {
            name: "Updata",
            path: "updata",
            meta: {
              title: "订单更新",
              requiresAuth: true
            },
            component: () => import("../pages/order/OrderUpdata.vue")
          }]
        }
      ]
    }
  ]
})

router.beforeEach(function (to, from, next) {
  //判断目标路由是否需要登录 才可访问!
  //当匹配的路由 项之中 有这个授权请求 那么就先从本地拿一下 本地的存储用户信息!
  if (to.matched.some(item => item.meta.requiresAuth)) {
    let userInfo = localStorage.getItem('userInfo') || {};
    try {
      userInfo = JSON.parse(userInfo)
    } catch (err) {
      userInfo = {}
    }
    // 判断当前用户信息是否包含token
    if (userInfo.authorization) {
      //发起请求 验证token是否过期
      $request.get("/jwtverify", {
          params: {
            authorization: userInfo.authorization
          }
        })
        .then(res => {
          // console.log("我是data啊", res.data.code)
          if (res.data.code === 1) {
            next()
          } else {
            next({
              path: "/login",
              query: {
                redirectTo: to.fullPath
              }
            })
          }
        })
      next();
    } else {
      next({
        path: "/login",
        query: {
          // 跳转到登录页面,并传递目标页面路径
          redirectTo: to.fullPath
        }
      })
    }
  } else {
    next();
  }
})

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
export default router;

Login.vue组件

  • 功能:

    • 先发起请求,渲染拿到验证码
    • 用户名,密码,验证码失去焦点验证
    • 把请求后的用户,存储在本地之中, 需要先定义一个 userInfo 变量 存储!
      • 避免userInfo 报错 const userInfo = localStorage.getItem("userInfo") || {}
    • 判断 登录成功时候,不仅需要存储到本地,还有判断 用户之前操作的 path路径 这从全局前置路由守卫那边拿取!
         //路由守卫 设置了 最开始 点击那里 就返回那个的path
          const { redirectTo = "/home" } = this.$route.query;
          this.$router.replace(redirectTo);
      
  • 数据的解析:

    • :rules=“rules” => 是检验的规则
    • ref=“ruleForm” => 这是为了拿到当前组件的内容
    • :model=“ruleForm” => 渲染的数据,以一个数组的形式!
    • prop => 接受父组件传递的数据(比如:ruleForm 里面的 username等等!)
    • v-model=“ruleForm.username” => 数据与表达绑定!
    • 验证码是返回一个html标签,因此需要使用 v-html去绑定 需要定义一个变量!
      <div v-html="vcodeSvg" class="vcode" @click="getVcode"></div>
    • getVcode => 获取验证码
    • handleLogin =>登录
 <div class="login-wrap">
    <el-form
      :rules="rules" 
      ref="ruleForm"
      :label-position="labelPosition"
      label-width="80px"
      class="login-form"
      :model="ruleForm"
    >
      <h2>用户登录</h2>
      <el-form-item label="用户名" prop="username">
        <el-input v-model="ruleForm.username"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input v-model="ruleForm.password"></el-input>
      </el-form-item>
      <el-form-item label="验证码" prop="vcode">
        <el-input v-model="ruleForm.vcode">
          <template v-slot:append>
            <div v-html="vcodeSvg" class="vcode" @click="getVcode"></div>
          </template>
        </el-input>
      </el-form-item>
      <el-form-item>
        <el-checkbox label="下次免登陆" v-model="ruleForm.mdl"></el-checkbox>
      </el-form-item>
      <el-button type="primary" class="login-btn" @click.prevent="handleLogin">登录</el-button>
    </el-form>
  </div>
  data() {
    var checkUsername = async (rule, value, callback) => {
      if (value.trim() == "") {
        return callback(new Error("用户名不能为空"));
      } else {
        callback();
      }
    };
    var checkUserpass = (rule, value, callback) => {
      if (value.trim() == "") {
        return callback(new Error("密码不能为空"));
      } else {
        callback();
      }
    };
    var checkUservcode = (rule, value, callback) => {
      if (value.trim() == "") {
        return callback(new Error("请输入验证码"));
      } else {
        callback();
      }
    };
    return {
      labelPosition: "left",
      vcodeSvg: "", //用于存放 请求回来的svg验证码这个html标签
      token: "",
      ruleForm: {
        username: "",
        password: "",
        vcode: "",
        mdl: true,
      },
      rules: {
        username: [{ validator: checkUsername, trigger: "blur" }],
        password: [{ validator: checkUserpass, trigger: "blur" }],
        vcode: [{ validator: checkUservcode, trigger: "blur" }],
      },
    };
  },
  created() {
    //一开始 发起请求 拿到验证码!
    this.getVcode();
  },
  methods: {
    async handleLogin() {
      // console.log(1, this.ruleForm);
      // 一进入登录界面的时候,就发起请求 请求验证码!
      const res = await this.$request.get("/mlogin", {
        params: {
          ...this.ruleForm,
        },
      });
      // console.log("我是点击登录", res.data);
      if (res.data.code === 1) {
        this.$message.success("登录成功");
        localStorage.setItem("userInfo", JSON.stringify(res.data.data));
        //路由守卫 设置了 最开始 点击那里 就返回那个的path
        const { redirectTo = "/home" } = this.$route.query;
        this.$router.replace(redirectTo);
      }
    },
    async getVcode() {
      const res = await this.$request.get("/vcode");
      this.vcodeSvg = res.data.data;
    },
  },

Home.vue组件

  • 在头部之中,有着登录按钮,点击去往登录,登录成功后,显示用户名 和 退出按钮!

    • 这时候就需要使用 template这个标签来作为载体,把v-if条件扔在这边 根据是否有token值(userInfo)
      假如有token值就是显示退出,否则就是登录
    • 那么这就需要监听 url 的变化 才做出决策了 watch:{ '$route.path'(to,from){} }
      • 假如是从 from.path === '/login" 那么就显示登录成功了 就显示那个退出 => 调用相应的方法 操作
      • 假如是从 to.path === ‘/login’ 那么就是需要去登录的状态,那么就显示登录!=> 调用相应的方法 操作
      watch: {
        "$route.path"(to, from) {
          // console.log("我是路由", to, from);
          if (from === "/login") {
            this.getUserList();
          }
          if (to === "login") {
            this.signOut();
          }
        },
      },
      
  • 数据分析

    • userInfo.authorization => 当前用户的token值
    • signOut => 退出操作
    • gotoLogin => 去往登录界面操作
 <div>
    <!-- 头部 -->
    <el-row :gutter="20" class="header-con">
      <el-col :span="12">
        <div class="grid-content bg-purple">
          <i class="el-icon-help"></i>
          小渣亮后台管理系统
        </div>
      </el-col>
      <el-col :span="12" style="text-align:right">
        <div class="login-box">
          <template v-if="userInfo.authorization">
            <span>{{ userInfo.username }}</span>
            <el-link type="primary" @click="signOut" style="margin-left:10px">退出</el-link>
          </template>
          <template v-else>
            <el-link type="primary" @click="gotoLogin" style="margin-right:10px">登录</el-link>
          </template>
        </div>
      </el-col>
    </el-row>

    <!-- 主体 -->
    <el-container style="height: 500px; border: 1px solid #eee">
      <!-- 侧边 -->
      <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
        <el-menu
          :default-active="activeIndex"
          :unique-opened="true"
          class="el-menu-demo"
          mode="vertical"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ff0"
          @select="change"
          :default-openeds="openMenu"
          router
        >
          <!-- active-text-color 高亮显示 -->
          <template v-for="item in menu">
            <!-- :index="绑定的是唯一的url路径"  注意点:就是 :key="需要写在渲染的组件上,不是元素模板!"-->
            <!-- v-if 存在这个子菜单,就渲染这个子菜单项! -->
            <el-menu-item :index="item.path" :key="item.path" v-if="!item.submenu">
              <i :class="item.icon"></i>
              {{item.text}}
            </el-menu-item>
            <!-- 带有子菜单的 -->
            <el-submenu :index="item.path" :key="item.path" v-else>
              <!-- 外层的父 -->
              <template v-slot:title>
                <i :class="item.icon"></i>
                {{item.text}}
              </template>
              <!-- 里面的子 -->
              <el-menu-item
                :index="item.path+subitem.path"
                v-for="subitem in item.submenu"
                :key="subitem.path"
              >{{subitem.text}}</el-menu-item>
            </el-submenu>
          </template>
        </el-menu>
      </el-aside>

      <!-- 左侧内容区 -->
      <el-main>
        <div style="padding:15px 0;">
          <router-view />
        </div>
      </el-main>
    </el-container>
  </div>
 name: "App",
  data() {
    return {
      menu: [
        {
          text: "首页",
          path: "/home",
          icon: "el-icon-s-home",
        },
        {
          text: "权限管理",
          path: "/power",
          icon: "el-icon-user-solid",
          submenu: [
            {
              text: "角色列表",
              path: "/poweruserlist",
            },
            {
              text: "权限列表",
              path: "/powerList",
            },
          ],
        },
        {
          text: "用户管理",
          path: "/user",
          icon: "el-icon-user-solid",
          submenu: [
            {
              text: "添加用户",
              path: "/add",
            },
            {
              text: "用户列表",
              path: "/list",
            },
          ],
        },
        {
          text: "商品管理",
          path: "/goods",
          icon: "el-icon-grape",
          submenu: [
            {
              text: "商品列表",
              path: "/list",
            },
            {
              text: "商品添加",
              path: "/add",
            },

            {
              text: "商品分类",
              path: "/type",
            },
          ],
        },
        {
          text: "订单管理",
          path: "/order",
          icon: "el-icon-s-order",
          submenu: [
            {
              text: "订单列表",
              path: "/list",
            },
            {
              text: "订单更新",
              path: "/updata",
            },
          ],
        },
      ],
      currentIndex: 0,
      activeIndex: "/home",
      openMenu: ["/user"],
      userInfo: {},
    };
  },
  created() {
    this.getUserList();
  },
  watch: {
    "$route.path"(to, from) {
      // console.log("我是路由", to, from);
      if (from === "/login") {
        this.getUserList();
      }
      if (to === "login") {
        this.signOut();
      }
    },
  },
  methods: {
    getUserList() {
      // 拿到本地的token值 ,防止出错 需要 在 ||一个空对象! 由于拿到的是字符串 需要转化为对象! 使用try的话 就不用判断了!
      let userInfo = localStorage.getItem("userInfo") || {};
      try {
        userInfo = JSON.parse(userInfo);
      } catch (err) {
        this.userInfo = {};
      }
      this.userInfo = userInfo;
      // console.log("我是token", userInfo, userInfo.authorization);
    },
    gotoLogin() {
      this.$router.push({
        name: "Login",
      });
    },
    change(path, idx) {
      this.activeIndex = path;
      this.currentIndex = idx; //这是点击事件修改的idx值!
      this.$router.push(path);
    },
    signOut() {
      //点击退出 ,也就是清除token
      localStorage.removeItem("userInfo");
      this.$message.success("退出成功");
      this.userInfo = {};
      this.$router.push("/login");
    },
  },
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值