vue路由登录拦截(vue router登录权限控制)

实现原理:

/:指向组件App.vue,它是最外层组件,下面的/login和/main对应的组件都会包含在其中;

/login:指向登录组件Login.vue;

/main:指向登录后组件Main.vue,其下会包括很多子组件来展示不同菜单项。

用户有没有登录需要给其指定状态(用islogin表示),当用户登录了,我们用localStorage在Login.vue文件中为其状态设定为1:

methods: {
      submitForm(formName) {
        let _this = this;
        let param = new URLSearchParams();
        param.append('username', _this.ruleForm.username);
        param.append('password', _this.ruleForm.password);

        this.$refs[formName].validate((valid) => {
          if (valid) {
            _this.$axios({
              method: 'POST',
              url: '/api/blog/check_login_status/',
              data: param
            })
            .then(res => {
              if(res.data.ret){
                localStorage.setItem("islogin", 1);
                console.log(localStorage.getItem("islogin"));
                _this.$router.push({path: "/main/form/radio"});
              }else{
                _this.$message('用户名或密码错误!');
                return false;
              }
            })
            .catch(err => {
              console.log(err);
            });
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }

这里可能会有Django后台获取不到前端axios-post请求提交的参数的问题,方法请参考:

https://blog.csdn.net/duansamve/article/details/101942106

退出时需要为其指定状态为0:

localStorage.setItem("islogin", 0);

路由拦截需要用到导航守卫,关键代码如下:

router.beforeEach((to, from, next) => {
  let islogin = localStorage.getItem("islogin");
  islogin = Boolean(Number(islogin));

  if(to.path == "/login"){
    if(islogin){
      next("/table");
    }else{
      next();
    }
  }else{
    // requireAuth:可以在路由元信息指定哪些页面需要登录权限
    if(to.meta.requireAuth && islogin) {
      next();
    }else{
      next("/login");
    }
  }
})

src/router/index.js中,可以看到所有路由如下:

let router = new Router({
  mode: 'hash',
  routes: [
    {
      path: '/',
      name: 'index',
      redirect: '/login'
    },
    {
      path: '/login',
      name: 'Login',
      component: Login,
      meta: {
        title: 'Login',
        icon: 'el-icon-eleme',
        requireAuth: true
      }
    },
    {
      path: '/main',
      name: 'main',
      component: Main,
      show: false,
      meta: {
        title: 'Main',
        icon: 'el-icon-eleme',
        requireAuth: true
      },
      children: [
        {
          path: '/main/form/radio',
          name: 'radio',
          component: Radio,
          show: true,
          meta: {
            title: 'Radio',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/form/checkbox',
          name: 'checkbox',
          component: Checkbox,
          show: true,
          meta: {
            title: 'Checkbox',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/data/table',
          name: 'table',
          component: Table,
          show: true,
          meta: {
            title: 'Table',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/data/tag',
          name: 'tag',
          component: Tag,
          show: true,
          meta: {
            title: 'Tag',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/button',
          name: 'button',
          component: Button,
          show: true,
          meta: {
            title: 'Button',
            icon: 'el-icon-s-order',
            requireAuth: true
          }
        },
        {
          path: '/main/tabs',
          name: 'tabs',
          component: Tabs,
          show: true,
          meta: {
            title: 'Tabs',
            icon: 'el-icon-s-ticket',
            requireAuth: true
          }
        },
        {
          path: '/main/echarts',
          name: 'echarts',
          component: Echarts,
          show: true,
          meta: {
            title: 'Echarts',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        }
      ]
    }
  ]
});

我们只需要把/main的子路由在菜单中循环出来,其它不需要显示,所以在Main.vue中增加如下方法提取/main的所有子路由:

  methods: {
    getRoutes() {
      for (let i = 0; i < this.$router.options.routes.length; i++) {
        if (this.$router.options.routes[i].name === "main") {
          return this.$router.options.routes[i].children;
        }
      }
    }
  }

关键文件代码:

router/index.js:

import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login'
import Main from '@/components/Main'
import Radio from '@/components/Radio'
import Checkbox from '@/components/Checkbox'
import Table from '@/components/Table'
import Tag from '@/components/Tag'
import Button from '@/components/Button'
import Tabs from '@/components/Tabs'
import Echarts from '@/components/Echarts'

Vue.use(Router);

let router = new Router({
  mode: 'hash',
  routes: [
    {
      path: '/',
      name: 'index',
      redirect: '/login'
    },
    {
      path: '/login',
      name: 'Login',
      component: Login,
      meta: {
        title: 'Login',
        icon: 'el-icon-eleme',
        requireAuth: true
      }
    },
    {
      path: '/main',
      name: 'main',
      component: Main,
      show: false,
      meta: {
        title: 'Main',
        icon: 'el-icon-eleme',
        requireAuth: true
      },
      children: [
        {
          path: '/main/form/radio',
          name: 'radio',
          component: Radio,
          show: true,
          meta: {
            title: 'Radio',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/form/checkbox',
          name: 'checkbox',
          component: Checkbox,
          show: true,
          meta: {
            title: 'Checkbox',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/data/table',
          name: 'table',
          component: Table,
          show: true,
          meta: {
            title: 'Table',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/data/tag',
          name: 'tag',
          component: Tag,
          show: true,
          meta: {
            title: 'Tag',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        },
        {
          path: '/main/button',
          name: 'button',
          component: Button,
          show: true,
          meta: {
            title: 'Button',
            icon: 'el-icon-s-order',
            requireAuth: true
          }
        },
        {
          path: '/main/tabs',
          name: 'tabs',
          component: Tabs,
          show: true,
          meta: {
            title: 'Tabs',
            icon: 'el-icon-s-ticket',
            requireAuth: true
          }
        },
        {
          path: '/main/echarts',
          name: 'echarts',
          component: Echarts,
          show: true,
          meta: {
            title: 'Echarts',
            icon: 'el-icon-s-marketing',
            requireAuth: true
          }
        }
      ]
    }
  ]
});

export default router

router.beforeEach((to, from, next) => {
  let islogin = localStorage.getItem("islogin");
  console.log(islogin);
  console.log(to.path);
  islogin = Boolean(Number(islogin));
 
  if(to.path == "/login"){
    if(islogin){
      next("/main/form/radio");
    }else{
      next();
    }
  }else{
    // requireAuth:可以在路由元信息指定哪些页面需要登录权限
    if(to.meta.requireAuth && islogin) {
      next();
    }else{
      next("/login");
    }
  }
})

Login.vue:

<template>
  <div id="login-container">
    <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="用户名" prop="checkUser">
        <el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="checkPass">
        <el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
        <el-button @click="resetForm('ruleForm')">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
 
<script>
  export default {
    name: 'Login',
    components: {
 
    },
    data() {
      var validateUser = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入用户名'));
        }  else {
          callback();
        }
      };

      var validatePass = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          callback();
        }
      };

      return {
        ruleForm: {
          username: '',
          password: ''
        },
        rules: {
          checkUser: [
            { validator: validateUser, trigger: 'blur' }
          ],
          checkPass: [
            { validator: validatePass, trigger: 'blur' }
          ]
        }
      };
    },
    methods: {
      submitForm(formName) {
        let _this = this;
        let param = new URLSearchParams();
        param.append('username', _this.ruleForm.username);
        param.append('password', _this.ruleForm.password);

        this.$refs[formName].validate((valid) => {
          if (valid) {
            _this.$axios({
              method: 'POST',
              url: '/api/blog/check_login_status/',
              data: param
            })
            .then(res => {
              if(res.data.ret){
                localStorage.setItem("islogin", 1);
                console.log(localStorage.getItem("islogin"));
                _this.$router.push({path: "/main/form/radio"});
              }else{
                _this.$message('用户名或密码错误!');
                return false;
              }
            })
            .catch(err => {
              console.log(err);
            });
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  }
</script>
 
<style scoped>
  body{
    margin: 0;
  }
  #login-container{
     400px;
    height: 300px;
    background: #e5e9f2;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -220px;
    margin-top: -170px;
    border-radius: 5px;
    padding-top: 40px;
    padding-right: 40px;
  }
</style>

Main.vue:


<template>
  <div id="app">
    <el-container style="height: 100%;">
      <el-header style="height: 80px;" :style="topBg">
        <Header/>
      </el-header>
      <el-container>
        <el-aside width="210px" :style="leftBg">
          <el-row class="tac">
            <el-col :span="24">
              <el-menu
                default-active="2"
                class="el-menu-vertical-demo"
                @open="handleOpen"
                @close="handleClose"
              >
                <template v-for="route in getRoutes()">
                    
                  <el-submenu
                    :key="route.path"
                    :index="route.path"
                    v-if="route.children && route.children.length"
                  >

                  <!-- 循环有子目录的菜单 -->
                    <template slot="title">
                      <i :class="route.meta.icon"></i>
                      <span>{{route.meta.title}}</span>
                    </template>
                    <el-menu-item-group>
                      <router-link :to="todo.path" :key="todo.path" v-for="todo in route.children">
                        <el-menu-item
                          :index="todo.path"
                        >{{todo.meta.title}}</el-menu-item>
                      </router-link>
                    </el-menu-item-group>
                  </el-submenu>
 
                  <!-- 循环没有子目录的菜单 -->
                  <router-link
                    :to="route.path"
                    :key="route.path"
                    v-else-if="!route.children && route.path != '/' && route.path != '/login'"
                  >
                    <el-menu-item :index="route.path">
                      <i :class="route.meta.icon"></i>
                      <span>{{route.meta.title}}</span>
                    </el-menu-item>
                  </router-link>

                </template>
              </el-menu>
            </el-col>
          </el-row>
        </el-aside>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
 
<script>
import Header from '@/components/Header'
export default {
  name: "App",
  data() {
    return {
      leftBg: {
        background: "#235d8b url(" + require("../assets/left-bg.png") + ") no-repeat scroll 0 bottom"
      },
      topBg: {
        background: "#235d8b url(" + require("../assets/top-bg.png") + ") no-repeat scroll right 0",
        height: '80px',
        fontSize: '32px',
        color: '#ffffff'
      }
    }
  },
  components: {
    Header
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
    getRoutes() {
      for (let i = 0; i < this.$router.options.routes.length; i++) {
        if (this.$router.options.routes[i].name === "main") {
          return this.$router.options.routes[i].children;
        }
      }
    }
  }
};
</script>
 
<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  height: 100%;
}
 
.el-header,
.el-footer {
  background-color: #b3c0d1;
  color: #333;
  line-height: 80px;
}
 
.el-aside {
  background-color: #d3dce6;
  color: #333;
}
 
.el-aside a{
  text-decoration: none;
}

.el-menu {
  background: none;
}
 
.el-main {
  background-color: #ffffff;
  color: #333;
}
 
body > .el-container {
  margin-bottom: 40px;
}
 
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}
 
.el-container:nth-child(7) .el-aside {
  line-height: 320px;
}
</style>

django代码:views.py

@csrf_exempt
def check_login_status(request):
    return_dict = {"ret": False}

    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'admin' and password == '123456':
            return_dict['ret'] = True

    return HttpResponse(json.dumps(return_dict), content_type="application/json")

vue从django跨域获取数据请参考博文:https://blog.csdn.net/duansamve/article/details/90310911

原文地址:https://www.cnblogs.com/samve/p/11619928.html