企业级Vue前端鉴权方案全解析|从Token到OAuth2.0,覆盖多端适配+权限管控

Vue前端鉴权的核心目标是控制不同用户对页面、接口、按钮的访问权限,避免未登录用户、低权限用户访问敏感资源(如个人中心、管理后台、核心接口)。前端鉴权仅为"辅助防护",不能替代后端鉴权(后端需对所有接口做权限校验,防止前端篡改),以下是Vue项目中最常用的5种鉴权方式,适配Vue2/Vue3,覆盖从简单到复杂的各类场景。

一、方式1:Token鉴权(最常用,适配移动端/PC端)

1. 核心原理

基于"令牌(Token)"实现鉴权,用户登录成功后,后端返回一个加密Token(如JWT、自定义Token),前端存储Token(localStorage/sessionStorage/cookie),后续所有请求通过请求头(如Authorization)携带Token,后端校验Token合法性,验证通过则允许访问,否则拒绝并跳转至登录页。

2. Vue实操(Vue3+Axios示例)

typescript 复制代码
// 1. 登录成功后存储Token
// api/login.ts 登录接口
import axios from 'axios';
export const login = async (username: string, password: string) => {
  const res = await axios.post('/api/login', { username, password });
  if (res.data.code === 200) {
    // 存储Token(根据需求选择存储方式)
    localStorage.setItem('token', res.data.data.token);
    // 存储用户信息(可选)
    localStorage.setItem('userInfo', JSON.stringify(res.data.data.user));
    return true;
  }
  return false;
};

// 2. Axios请求拦截器,统一携带Token
// utils/request.ts
import axios from 'axios';
import router from '@/router';

const request = axios.create({
  baseURL: import.meta.env.VUE_APP_BASE_API,
  timeout: 5000
});

// 请求拦截器:添加Token
request.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`; // 格式需与后端一致
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 响应拦截器:处理Token失效
request.interceptors.response.use(
  (response) => response,
  (error) => {
    // Token过期、无效,跳转至登录页,清除本地存储
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      localStorage.removeItem('userInfo');
      router.push('/login');
      ElMessage.error('登录已过期,请重新登录');
    }
    return Promise.reject(error);
  }
);

export default request;

// 3. 路由守卫:拦截未登录用户访问需鉴权页面
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { ElMessage } from 'element-plus';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/login', component: () => import('@/views/Login.vue') },
    { 
      path: '/home', 
      component: () => import('@/views/Home.vue'),
      meta: { requiresAuth: true } // 标记需鉴权页面
    },
    { 
      path: '/profile', 
      component: () => import('@/views/Profile.vue'),
      meta: { requiresAuth: true }
    }
  ]
});

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 需鉴权且无Token,跳转登录页
  if (to.meta.requiresAuth && !localStorage.getItem('token')) {
    ElMessage.warning('请先登录');
    next('/login');
  } else {
    next();
  }
});

export default router;

3. 适用场景

移动端H5、PC端后台管理系统、前后端分离项目(最主流的鉴权方式),适配大多数Vue项目。

4. 注意事项

  • Token存储选择:localStorage(持久化,关闭浏览器不丢失,易受XSS攻击)、sessionStorage(会话级,关闭浏览器丢失,相对安全)、cookie(可设置httpOnly,防XSS,需处理跨域);
  • Token过期处理:可实现Token刷新机制(如刷新Token、无感续期),避免用户频繁登录;
  • 安全风险:Token需加密传输(HTTPS),避免明文传输被窃取;前端仅做Token存储和携带,权限校验必须依赖后端。

二、方式2:Cookie鉴权(传统方式,适配PC端)

1. 核心原理

用户登录成功后,后端通过Set-Cookie响应头,将鉴权信息(如用户ID、会话ID)写入浏览器Cookie,后续前端请求会自动携带Cookie(浏览器默认行为),后端通过Cookie中的信息校验用户身份和权限,无需前端手动处理。

2. Vue实操要点

typescript 复制代码
// 1. 登录接口(无需前端存储,后端直接写入Cookie)
export const login = async (username: string, password: string) => {
  const res = await axios.post('/api/login', { username, password });
  // 后端通过Set-Cookie写入Cookie,前端无需额外操作
  return res.data.code === 200;
};

// 2. 路由守卫(判断Cookie是否存在,或通过接口校验Cookie有效性)
router.beforeEach(async (to, from, next) => {
  if (to.meta.requiresAuth) {
    // 方式1:简单判断Cookie是否存在(不严谨)
    const hasCookie = document.cookie.includes('sessionId'); // sessionId为后端设置的Cookie键名
    // 方式2:调用接口校验Cookie有效性(推荐,避免Cookie伪造)
    const res = await request.get('/api/checkCookie');
    if (res.data.code === 200) {
      next();
    } else {
      next('/login');
      ElMessage.warning('请先登录');
    }
  } else {
    next();
  }
});

3. 适用场景

PC端传统项目、前后端不分离项目,或对安全性要求较高(可设置httpOnly、secure)的场景。

4. 注意事项

  • Cookie配置:后端需设置httpOnly: true(防止XSS攻击,前端无法通过JS获取Cookie)、secure: true(仅HTTPS传输)、sameSite: 'strict'(防止CSRF攻击);
  • 跨域问题:跨域场景下,需后端配置Access-Control-Allow-Credentials: true,前端axios配置withCredentials: true,否则Cookie无法跨域携带;
  • 局限性:移动端H5适配性较差(部分浏览器对Cookie支持不友好),不适用于多端统一项目。

三、方式3:路由鉴权(页面级控制,辅助鉴权)

1. 核心原理

基于Vue Router的路由守卫(全局守卫、路由独享守卫、组件内守卫),控制用户对页面的访问权限,本质是"前端拦截",需配合Token/Cookie鉴权使用,不能单独作为鉴权方式(防止用户手动修改路由跳转)。

2. 进阶实操(Vue3,区分角色权限)

javascript 复制代码
// 1. 路由配置(区分不同角色权限)
const routes = [
  { path: '/login', component: () => import('@/views/Login.vue') },
  { 
    path: '/home', 
    component: () => import('@/views/Home.vue'),
    meta: { requiresAuth: true, roles: ['admin', 'user'] } // 允许admin和user访问
  },
  { 
    path: '/admin', 
    component: () => import('@/views/Admin.vue'),
    meta: { requiresAuth: true, roles: ['admin'] } // 仅允许admin访问
  },
  { 
    path: '/403', 
    component: () => import('@/views/403.vue') // 无权限页面
  }
];

// 2. 全局前置守卫(校验角色权限)
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token');
  const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
  
  // 无需鉴权,直接放行
  if (!to.meta.requiresAuth) {
    next();
    return;
  }
  
  // 需鉴权但无Token,跳转登录
  if (!token) {
    next('/login');
    ElMessage.warning('请先登录');
    return;
  }
  
  // 需鉴权且有Token,校验角色权限
  if (to.meta.roles && to.meta.roles.length) {
    if (to.meta.roles.includes(userInfo.role)) {
      next(); // 角色匹配,放行
    } else {
      next('/403'); // 无权限,跳转403页面
    }
  } else {
    next(); // 无角色限制,放行
  }
});

3. 适用场景

所有Vue项目,作为Token/Cookie鉴权的辅助方式,实现页面级、角色级的访问控制(如管理员页面仅允许管理员访问)。

4. 注意事项

  • 不能单独使用:路由鉴权仅在前端拦截,用户可通过修改URL、调试工具跳过拦截,必须配合后端接口鉴权;
  • 角色权限存储:用户角色信息建议存储在Token中(加密),或通过接口实时获取,避免前端篡改角色;
  • 403页面:无权限时跳转至403页面,提升用户体验,避免直接白屏。

四、方式4:组件级鉴权(按钮/内容级控制)

1. 核心原理

在组件内部,根据用户角色、权限,控制按钮、内容的显示/隐藏(如管理员显示"删除"按钮,普通用户隐藏),实现更精细的权限控制,常用两种实现方式:自定义指令、组件封装。

2. Vue实操(两种方式)

方式A:自定义指令(推荐,复用性强)

javascript 复制代码
// 1. 注册自定义权限指令(utils/directive.ts)
import { DirectiveBinding } from 'vue';

// 权限指令:v-permission="['admin']"
const permission = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
    const userRole = userInfo.role || '';
    // 校验用户角色是否在允许范围内
    if (!binding.value.includes(userRole)) {
      el.style.display = 'none'; // 无权限,隐藏元素
      // 或直接移除元素
      // el.parentNode?.removeChild(el);
    }
  }
};

// 注册全局指令(main.ts)
import { createApp } from 'vue';
import App from './App.vue';
import { permission } from './utils/directive';

const app = createApp(App);
app.directive('permission', permission);
app.mount('#app');

// 2. 组件中使用
<template>
  <el-button v-permission="['admin']" type="danger">删除</el-button>
  <el-button v-permission="['admin', 'user']" type="primary">编辑</el-button>
</template>

方式B:组件封装(灵活,可自定义逻辑)

xml 复制代码
<template>
  <div v-if="hasPermission">
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
import { defineProps } from 'vue';

// 接收允许的角色列表
const props = defineProps({
  roles: {
    type: Array,
    default: () => []
  }
});

// 校验权限
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
const hasPermission = props.roles.includes(userInfo.role || '');
</script>

// 组件中使用
<template>
  <Permission roles="['admin']">
    <el-button type="danger">删除</el-button>
  </Permission>
</template>

3. 适用场景

需要精细权限控制的场景(如管理后台、多角色系统),控制按钮、菜单、内容的显示/隐藏。

4. 注意事项

  • 仅隐藏元素,不禁止功能:前端隐藏无权限按钮,但用户仍可通过调试工具启用按钮、调用接口,必须配合后端接口鉴权;
  • 权限同步:用户角色变更后,需刷新页面或重新获取用户信息,确保权限实时生效;
  • 指令复用:将权限指令抽离为全局指令,提升代码复用性,避免重复开发。

五、方式5:OAuth2.0鉴权(第三方登录,适配多平台)

1. 核心原理

基于OAuth2.0协议,实现第三方登录鉴权(如微信、QQ、支付宝登录),用户通过第三方平台授权后,前端获取授权码,传递给后端,后端通过授权码获取第三方平台的用户信息,生成前端所需的Token,后续鉴权流程与Token鉴权一致。

2. Vue实操(微信H5登录示例)

javascript 复制代码
// 1. 跳转微信授权页面
const wechatLogin = () => {
  const appId = '你的微信公众号appId';
  const redirectUri = encodeURIComponent('你的回调页面地址'); // 如:https://xxx.com/wechat/callback
  const scope = 'snsapi_userinfo'; // 授权范围
  // 跳转微信授权页面
  window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=123#wechat_redirect`;
};

// 2. 回调页面接收授权码,获取Token
// views/WechatCallback.vue
import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { getWechatToken } from '@/api/login';

const router = useRouter();

onMounted(() => {
  // 从URL中获取授权码code
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  if (code) {
    // 调用后端接口,通过code获取Token
    getWechatToken(code).then(res => {
      if (res.data.code === 200) {
        localStorage.setItem('token', res.data.data.token);
        localStorage.setItem('userInfo', JSON.stringify(res.data.data.user));
        // 跳转首页
        router.push('/home');
      }
    });
  }
});

3. 适用场景

需要第三方登录(微信、QQ、支付宝)的场景,如移动端H5、小程序内嵌H5,提升用户登录体验。

4. 注意事项

  • 授权配置:需在第三方平台(如微信公众平台)配置回调地址,确保回调地址与配置一致,否则授权失败;
  • 授权范围:根据需求选择授权范围(如snsapi_base仅获取用户openid,snsapi_userinfo获取用户昵称、头像等信息);
  • 安全校验:后端需校验授权码的合法性,防止伪造授权码获取Token。

六、核心总结(选型建议+避坑核心)

1. 选型建议

  • 优先选择:Token鉴权(适配多端、前后端分离,最主流);
  • PC端传统项目:Cookie鉴权(配合httpOnly,安全性高);
  • 多角色精细控制:Token鉴权 + 路由鉴权 + 组件级鉴权(组合使用);
  • 第三方登录场景:OAuth2.0鉴权(配合Token鉴权)。

2. 避坑核心(必看)

  • 前端鉴权仅为辅助:所有敏感接口、权限控制,必须在后端做校验,防止前端篡改数据、跳过拦截;
  • Token/Cookie安全:使用HTTPS传输,避免明文泄露;合理设置过期时间,定期刷新;
  • 权限同步:用户登录、退出、角色变更后,需及时清理本地存储(Token、用户信息),避免权限残留;
  • 用户体验:无权限、Token过期时,给出清晰提示(如"请先登录""无权限访问"),并跳转至对应页面(登录页、403页)。
相关推荐
漫游的渔夫1 小时前
前端开发者做 Agent:模型说执行就执行?先加 3 道闸门再碰真实业务
前端·人工智能·typescript
亲亲小宝宝鸭1 小时前
从Vben-Admin里面学习hooks
前端·vue.js
Mintopia1 小时前
MSW Mock Feature-First 方案
前端·架构
sin6031 小时前
Talk is cheap 之后:AI Agent 时代,程序员真正要交付什么?
前端
Ticnix1 小时前
手把手教你在 Next.js 中接入本地大模型,实现 ChatGPT 同款流式对话
前端·next.js
ZC跨境爬虫1 小时前
跟着 MDN 学 HTML day_18:(HTML 表格进阶特性与无障碍——从标题结构到屏幕阅读器适配)
前端·笔记·ui·html·音视频
沐 修1 小时前
前端调试 - 获取下拉框元素 F12 延时断点操作记录 - 秒杀其他所谓的F8和手速快操作
前端
天蓝色的鱼鱼1 小时前
当AI开始替我写代码,我还要纠结选Vue还是React吗?
vue.js·react.js·ai编程
恋猫de小郭1 小时前
AI 时代开源协议将消亡,malus 讽刺性展示了这一点
前端·人工智能·ai编程