You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js基于角色的登录与权限控制实现方案咨询

基于角色的登录重定向与权限配置方案(Vue.js)

问题背景

我是Vue.js初学者,正在开发一个包含基于角色的登录和报表列表功能的项目,设计了HAuser和Supplier两类角色,需要实现:

  1. 不同角色用户登录后自动跳转到对应页面
  2. 配置差异化的页面访问权限
  3. 报表列表根据角色过滤展示

已完成的代码实现

登录组件

<template>
  <div class="auth-inner">
    <h3>{{ userType }} {{ $t('Login') }}</h3>
    <form v-on:submit.prevent="onSubmit">
      <div class="form-group">
        <label>{{ $t('username') }}</label>
        <input v-model="loginForm.username" type="username" class="form-control" :placeholder="$t('username')" />
      </div>

      <div class="form-group">
        <label>{{ $t('password') }}</label>
        <input v-model="loginForm.password" type="password" class="form-control" :placeholder="$t('password')" />
      </div>
      <div class="d-grid gap-2">
        <button class="btn btn-primary" type="submit">{{ $t('Login') }}</button>
        <vue-recaptcha 
          ref="recaptcha"
          @verify="onCaptchaVerified" 
          @expired="onCaptchaExpired" 
          sitekey="6LfCmnEhAAAAAIsEHJx8QXbUHIAvIsuuwQW4JGj_" 
          class="mt-3" />
      </div>
      <div v-if="showErrorMessage" class="recaptcha-error-message"><strong>{{ $t('recaptcha-error-message') }}</strong></div>
    </form>
  </div>
</template>

<script>
import { VueRecaptcha } from 'vue-recaptcha';
export default {
  name: 'login',
  props: ['userType'],
  components: { VueRecaptcha },
  data(){
    return{
      showErrorMessage: false,
      recaptchaToken: '',
      status: '',
      sucessfulServerResponse: '',
      serverError: '',
      recaptchaVerified: false,
      submitted: false,
      loading: false,
      returnUrl: '',
      error: '',
      loginForm: {
        username: '',
        password: '',
      }
    }
  },
  methods: {
    onCaptchaVerified: function (recaptchaToken) {
      this.showErrorMessage = false;
      this.loginForm.recaptchaVerified = true;
      this.recaptchaToken = recaptchaToken;
    },
    
    onCaptchaExpired: function () {
      this.loginForm.recaptchaVerified = false;
      this.$refs.recaptcha.reset();
      if (!this.loginForm.recaptchaVerified) {
        this.showErrorMessage = true;
        return; // 阻止表单提交
      }
    },

    onSubmit () {
      if (!this.loginForm.recaptchaVerified) {
        this.showErrorMessage = true;
        return; // 阻止表单提交
      }
    }
  }
}
</script>

<style>
.recaptcha-error-message {
  color: red;
}
.auth-inner {
  width: 450px;
  margin:auto;
  background: #ffffff;
  box-shadow: 0px 14px 80px rgba(34, 35, 58,0.2);
  padding: 40px 55px 45px 55px;
  border-radius: 15px;
  transition: all .3s;
}
</style>

不同用户视图

HAuser视图

<template>
  <Login userType='HAuser'/>
</template>

<script>
import Login from '@/components/Login.vue';

export default {
  components: {
    Login
  }
}
</script>

<style>
</style>

Supplier视图

<template>
  <Login userType='Supplier' />
</template>

<script>
import Login from '@/components/Login.vue';

export default {
  components: {
    Login
  },
}
</script>

<style>
</style>

报表常量

{
    "reports": [
        { "type": "Supplier", "name": "Summary of Users Last Access Time" },
        { "type": "Supplier", "name": "Capital Budget Report - Grand Summary" },
        { "type": "HAuser", "name": "(Workflow) Report on Site Directions/Memo Status" },
        { "type": "HAuser", "name": "Yearly Budget Forecast Working Sheet by Project" },
        { "type": "HAuser", "name": "Report on SWD's Lotteries Fund Project Under Block" }
    ]
}

解决方案

一、完善登录逻辑与登录后重定向

在登录组件的onSubmit方法中完成登录请求,获取后端返回的用户角色并存储,再根据角色跳转对应页面:

<script>
import { VueRecaptcha } from 'vue-recaptcha';
export default {
  // 保留原有代码
  methods: {
    // 保留原有验证码方法
    async onSubmit () {
      if (!this.loginForm.recaptchaVerified) {
        this.showErrorMessage = true;
        return;
      }
      this.loading = true;
      try {
        // 发送登录请求,携带用户名、密码、验证码及角色类型
        const response = await this.$axios.post('/api/login', {
          ...this.loginForm,
          userType: this.userType,
          recaptchaToken: this.recaptchaToken
        });
        const { userInfo, token } = response.data;
        // 存储用户信息到localStorage(推荐用Vuex+持久化插件替代)
        localStorage.setItem('user', JSON.stringify(userInfo));
        localStorage.setItem('token', token);
        // 根据角色重定向到对应首页
        if (userInfo.role === 'HAuser') {
          this.$router.push('/hauser-dashboard');
        } else if (userInfo.role === 'Supplier') {
          this.$router.push('/supplier-dashboard');
        }
      } catch (err) {
        this.error = err.response?.data?.message || '登录失败';
      } finally {
        this.loading = false;
      }
    }
  }
}
</script>

二、配置路由权限控制

通过Vue Router的全局导航守卫拦截路由,校验用户角色权限:

1. 定义带权限的路由规则

在router/index.js中配置路由,给需要权限的页面添加meta.roles字段:

import Vue from 'vue';
import VueRouter from 'vue-router';
import HAuserDashboard from '@/views/HAuserDashboard.vue';
import SupplierDashboard from '@/views/SupplierDashboard.vue';
import HAuserLogin from '@/views/HAuserLogin.vue';
import SupplierLogin from '@/views/SupplierLogin.vue';
import ReportList from '@/views/ReportList.vue';
import NotFound from '@/views/NotFound.vue';
import Forbidden from '@/views/Forbidden.vue';

Vue.use(VueRouter);

const routes = [
  { path: '/hauser-login', component: HAuserLogin },
  { path: '/supplier-login', component: SupplierLogin },
  // HAuser专属页面
  {
    path: '/hauser-dashboard',
    component: HAuserDashboard,
    meta: { roles: ['HAuser'], requiresAuth: true }
  },
  // Supplier专属页面
  {
    path: '/supplier-dashboard',
    component: SupplierDashboard,
    meta: { roles: ['Supplier'], requiresAuth: true }
  },
  // 报表列表页面(双角色可访问,内容差异化)
  {
    path: '/reports',
    component: ReportList,
    meta: { roles: ['HAuser', 'Supplier'], requiresAuth: true }
  },
  { path: '/403', component: Forbidden },
  { path: '*', component: NotFound }
];

const router = new VueRouter({ routes });

export default router;

2. 添加全局导航守卫

在router/index.js末尾添加权限校验逻辑:

router.beforeEach((to, from, next) => {
  // 无需权限的页面直接放行
  if (!to.meta.requiresAuth) {
    next();
    return;
  }
  // 检查用户是否已登录
  const user = JSON.parse(localStorage.getItem('user'));
  if (!user) {
    // 未登录跳转到对应角色登录页(可改为登录选择页)
    next('/hauser-login');
    return;
  }
  // 校验角色权限
  if (to.meta.roles.includes(user.role)) {
    next();
  } else {
    // 无权限跳转至403页面
    next('/403');
  }
});

三、报表列表按角色过滤展示

在报表列表组件中,根据当前用户角色过滤报表数据:

<template>
  <div class="report-list">
    <h2>我的报表</h2>
    <ul>
      <li v-for="report in filteredReports" :key="report.name" class="report-item">
        {{ report.name }}
      </li>
    </ul>
  </div>
</template>

<script>
import reportsData from '@/constants/reports.json';
export default {
  data() {
    return {
      allReports: reportsData.reports
    };
  },
  computed: {
    filteredReports() {
      const user = JSON.parse(localStorage.getItem('user'));
      // 过滤出当前角色对应的报表
      return this.allReports.filter(report => report.type === user.role);
    }
  }
};
</script>

<style scoped>
.report-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
}
</style>

四、优化建议

  • 用Vuex管理用户状态:替代localStorage,配合vuex-persistedstate插件实现状态持久化,便于全局调用用户信息。
  • 添加退出登录逻辑:清除存储的用户信息和token,跳转至登录页。
  • 统一错误处理:对登录失败、权限校验失败等场景添加友好提示。

内容的提问来源于stack exchange,提问作者ka hei tong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 11:48:25