当前位置: 首页 > news >正文

单点登录(SSO)前端(Vue2.X)改造

摘要

本文详细阐述了前端管理系统(manager-ui)从传统用户名密码登录方式向单点登录(SSO)方式的改造过程。文章将从原有登录机制、SSO实现方案、技术架构变更和核心代码实现等方面进行深入分析,为类似的系统改造提供技术参考。

1. 系统背景

1.1 项目概述

  • 项目名称: manager-ui
  • 技术栈: Vue.js 2.6.10 + Vue Router 3.0.2 + Vuex 3.1.0 + Element UI 2.13.2
  • 架构模式: SPA (单页应用) + 前后端分离
  • 改造目标: 实现企业级单点登录,提升用户体验,统一身份认证

1.2 改造动机

  1. 用户体验优化: 减少重复登录,实现一次登录全平台访问
  2. 安全性提升: 统一认证中心,集中权限管理
  3. 运维效率: 降低多系统维护成本
  4. 合规要求: 满足企业统一身份认证规范

2. 原有登录机制分析

2.1 传统登录流程

原有系统采用经典的用户名密码登录模式,具体流程如下:

2.1.0 传统登录时序图
用户浏览器Vue应用Vuex Store后端API路由守卫传统登录流程1. 访问业务页面2. 路由拦截检查3. 检查token(无token)4. 重定向到登录页5. 显示登录页面6. 输入用户名密码7. 点击登录按钮8. 表单验证9. 调用Login Action10. 发送登录请求11. 验证用户凭据12. 返回token13. 存储token到Cookie14. 更新Vuex状态15. 登录成功16. 路由跳转17. 检查token(有token)18. 获取用户信息19. 请求用户详情20. 返回用户信息21. 生成动态路由22. 跳转到目标页面23. 显示业务页面用户浏览器Vue应用Vuex Store后端API路由守卫
2.1.1 登录组件结构
<!-- /src/views/login.vue -->
<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules"><!-- 支持两种登录方式 --><div v-show="userRequired"><!-- 用户名密码登录 --><el-form-item prop="username"><el-input v-model="loginForm.username" placeholder="请输入用户名" /></el-form-item><el-form-item prop="password"><el-input v-model="loginForm.password" type="password" placeholder="请输入密码" /></el-form-item></div><div v-show="!userRequired"><!-- 手机验证码登录 --><el-form-item prop="mobile"><el-input v-model="loginForm.mobile" placeholder="请输入手机号" /></el-form-item><el-form-item prop="smsCode"><el-input v-model="loginForm.smsCode" placeholder="请输入验证码" /></el-form-item></div></el-form></div>
</template>
2.1.2 登录业务逻辑
// 登录处理函数
handleLogin: function() {this.$refs.loginForm.validate(valid => {if (valid) {if (this.changeLogins === '0') {// 用户名密码登录this.loading = truethis.$store.dispatch('Login', this.loginForm).then(() => {this.$router.push({ path: this.redirect || '/' })}).catch(() => {this.loading = false})} else {// 短信验证码登录this.$store.dispatch('smsLogin', this.loginForm).then(() => {this.$router.push({ path: this.redirect || '/' })})}}})
}
2.1.3 Vuex登录Actions
// /src/store/modules/user.js
actions: {// 传统登录Login({ commit }, userInfo) {let { username = '', password, verificationCode, wordKey } = userInfousername = username.trim()return new Promise((resolve, reject) => {login(username, password, verificationCode, wordKey).then(res => {setToken(res.data.token)  // 存储Token到Cookiecommit('SET_TOKEN', res.data.token)  // 更新Vuex状态resolve()}).catch(error => {reject(error)})})}
}

2.2 权限验证机制

2.2.1 路由守卫
// /src/permission.js
router.beforeEach((to, from, next) => {NProgress.start()if (getToken()) {/* 已有token */if (to.path === '/login') {next({ path: '/' })} else {if (store.getters.roles.length === 0) {// 获取用户信息和权限store.dispatch('GetInfo').then(res => {const roles = res.data.rolesstore.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {router.addRoutes(accessRoutes) // 动态添加路由next({ ...to, replace: true })})})} else {next()}}} else {// 无token,跳转登录页next(`/login?redirect=${to.fullPath}`)}
})

2.3 传统方式的局限性

  1. 重复登录: 每个系统都需要单独登录
  2. 密码管理复杂: 用户需要记住多套账号密码
  3. 安全风险: 密码泄露风险分散到各个系统
  4. 运维成本: 每个系统独立维护用户数据

3. SSO改造方案设计

3.1 技术架构

3.1.1 SSO系统架构图
数据层
业务应用层
认证层
门户层
用户层
用户数据库
Redis缓存
业务系统A
manager-ui
业务系统B
Other Systems
业务系统C
Mobile Apps
认证中心
Auth Center
JWT Token服务
门户系统
Portal System
用户
3.1.2 SSO交互架构图
业务系统
认证中心
门户系统
客户端
SSO页面
/sso
路由守卫
Vuex Store
业务页面
认证服务器
Token生成器
用户存储
门户首页
应用菜单
浏览器
3.1.3 核心组件设计
  1. SSO页面组件 (/src/views/sso/index.vue)
  2. 路由配置更新 (/src/router/index.js)
  3. 权限验证增强 (/src/permission.js)
  4. 请求拦截优化 (/src/utils/request.js)

3.2 JWT Token机制

3.2.1 Token结构
// JWT Token 结构
{header: {"alg": "HS256","typ": "JWT"},payload: {"sub": "userId",           // 用户ID"username": "username",    // 用户名"roles": ["role1", "role2"], // 角色"permissions": ["perm1"],   // 权限"exp": 1640995200,         // 过期时间"iat": 1640908800          // 签发时间},signature: "signature"
}

4. SSO核心实现

4.1 SSO页面组件

4.1.1 组件模板
<!-- /src/views/sso/index.vue -->
<template><div class="sso-container"><div class="loading-wrapper"><!-- 加载状态 --><div v-if="loading" class="loading-content"><div class="loading-spinner"><i class="el-icon-loading"></i></div><p class="loading-text">正在验证登录信息,请稍候...</p></div><!-- 错误状态 --><div v-if="!loading && error" class="error-content"><div class="error-icon"><i class="el-icon-warning"></i></div><p class="error-message">{{ error }}</p><div class="error-actions"><el-button type="primary" @click="redirectToLogin">返回登录页面</el-button><el-button @click="redirectToPortal" v-if="showPortalButton">返回门户系统</el-button></div></div></div></div>
</template>
4.1.2 SSO业务逻辑
export default {name: 'SSO',data() {return {loading: true,error: null,token: null,showPortalButton: false}},created() {this.handleSSOLogin()},methods: {async handleSSOLogin() {try {console.log('开始SSO登录流程')// 1. 从URL参数获取token和重定向地址this.token = this.$route.query.tokenconst redirect = this.$route.query.redirect || '/index'if (!this.token) {throw new Error('缺少token参数,无法完成单点登录')}// 2. 验证token格式(JWT格式验证)if (!this.validateToken(this.token)) {throw new Error('token格式无效,请联系系统管理员')}// 3. 存储token到CookiesetToken(this.token)// 4. 更新Vuex状态this.$store.commit('SET_TOKEN', this.token)// 5. 获取用户信息await this.$store.dispatch('GetInfo')// 6. 生成动态路由const roles = this.$store.getters.rolesconst accessRoutes = await this.$store.dispatch('GenerateRoutes', { roles })// 7. 添加路由到路由器this.$router.addRoutes(accessRoutes)// 8. 标记为SSO登录sessionStorage.setItem('sso-login', 'true')// 9. 跳转到目标页面setTimeout(() => {this.$router.replace({ path: redirect })}, 1500)} catch (error) {console.error('SSO登录失败:', error)this.error = error.message || '单点登录失败,请重新登录'this.loading = falsethis.showPortalButton = true}},// JWT Token格式验证validateToken(token) {if (!token || typeof token !== 'string') {return false}const parts = token.split('.')if (parts.length !== 3) {return false}try {const payload = JSON.parse(atob(parts[1]))// 检查token是否过期if (payload.exp && payload.exp * 1000 < Date.now()) {return false}return true} catch (e) {return false}}}
}

4.2 路由配置更新

4.2.1 添加SSO路由
// /src/router/index.js
export const constantRoutes = [{path: '/login',component: () => import('@/views/login'),hidden: true},// SSO路由配置{path: '/sso',component: () => import('@/views/sso/index'),hidden: true,meta: { title: '单点登录' }}// ... 其他路由
]

4.3 权限验证增强

4.3.1 路由白名单更新
// /src/permission.js
// 添加SSO路径到白名单
const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/middleware', '/forgetPassword', '/sso']router.beforeEach((to, from, next) => {NProgress.start()if (getToken()) {// 已有token的处理逻辑保持不变// ...} else {// 没有tokenif (whiteList.indexOf(to.path) !== -1) {// 在免登录白名单,直接进入(包括SSO页面)next()} else {next(`/login?redirect=${to.fullPath}`)}}
})

4.4 请求拦截优化

4.4.1 Token失效处理
// /src/utils/request.js - 响应拦截器优化
service.interceptors.response.use(res => {if (!res.data) return const code = res.data.code || '000000'const message = errorCode[code] || res.data.msg || errorCode['default']if (code === '403000') {// Token失效处理const isFromSSO = sessionStorage.getItem('sso-login') === 'true'const isCurrentSSO = window.location.pathname === '/sso'// 避免在SSO页面重复弹框if (!isCurrentSSO) {MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录','系统提示',{confirmButtonText: '重新登录',cancelButtonText: '取消',type: 'warning'}).then(() => {// 清除SSO标记,跳转到登录页sessionStorage.removeItem('sso-login')store.dispatch('LogOut').then(() => {location.href = '/login'})})}return Promise.reject(new Error(message))}// ... 其他错误处理
})

5. SSO跳转流程详解

5.1 SSO单点登录时序图

5.1.1 完整SSO登录流程时序图
用户门户系统认证中心浏览器Vue应用(SSO页面)Vuex Store后端API路由守卫SSO单点登录流程1. 用户在门户系统登录2. 请求认证3. 验证用户凭据4. 返回认证成功5. 点击业务系统链接6. 检查登录状态7. 生成JWT Token8. 返回Token9. 跳转到业务系统http://10.1.73.208/sso?token=xxx&redirect=/index10. 加载SSO页面11. 解析URL参数获取token12. 验证JWT Token格式13. 存储token到Cookie14. 更新Vuex状态(SET_TOKEN)15. 调用GetInfo获取用户信息16. 请求用户详情17. 返回用户信息和权限18. 更新用户状态19. 调用GenerateRoutes生成路由20. 根据权限生成动态路由21. 添加路由到路由器22. 设置SSO登录标记23. 跳转到目标页面(/index)24. 显示业务页面(登录成功)整个过程用户无感知,自动完成登录用户门户系统认证中心浏览器Vue应用(SSO页面)Vuex Store后端API路由守卫
5.1.2 SSO vs 传统登录对比时序图
  1. 前端Token验证机制

    // SSO页面验证Token有效性
    validateToken(token) {const parts = token.split('.')if (parts.length !== 3) return falsetry {const payload = JSON.parse(atob(parts[1]))// 检查过期时间if (payload.exp * 1000 < Date.now()) return falsereturn true} catch (e) {return false}
    }
    
  2. 状态同步机制

    // 同步更新应用状态
    setToken(this.token)                          // Cookie存储
    this.$store.commit('SET_TOKEN', this.token)   // Vuex状态
    await this.$store.dispatch('GetInfo')         // 获取用户信息
    
  3. 路由权限自动配置

    // 动态生成并添加路由
    const roles = this.$store.getters.roles
    const accessRoutes = await this.$store.dispatch('GenerateRoutes', { roles })
    this.$router.addRoutes(accessRoutes)
    
5.2.2 跳转URL结构解析
http://10.1.73.208/sso?token=xxx&redirect=/index
│                  │   │         │
│                  │   │         └─ 登录成功后跳转的目标页面
│                  │   └─ JWT Token(包含用户身份和权限信息)
│                  └─ SSO处理页面
└─ 业务系统域名

参数说明:

  • token: JWT格式的身份令牌,包含用户信息和权限
  • redirect: 登录成功后的跳转目标,默认为/index
5.2.3 自动登录成功的技术保障
  1. Token预验证:在跳转前确保Token有效性
  2. 状态预设置:提前配置好用户权限和路由
  3. 无缝切换:用户感知不到登录过程
  4. 错误兜底:Token无效时自动降级到传统登录

6. 安全性考量

6.1 Token安全机制

  1. 时效性控制

    // Token过期时间检查
    if (payload.exp && payload.exp * 1000 < Date.now()) {throw new Error('Token已过期')
    }
    
  2. HTTPS传输

    • 所有Token传输必须通过HTTPS加密
    • 防止中间人攻击和Token泄露
  3. 跨域安全

    // 验证Token来源域名
    const allowedDomains = ['portal.company.com', 'auth.company.com']
    

6.2 防重放攻击

  1. Token唯一性:每次登录生成唯一Token
  2. 时间窗口:限制Token使用时间窗口
  3. 请求签名:关键操作增加请求签名验证

7. 性能优化

7.1 加载性能

  1. 路由懒加载

    component: () => import('@/views/sso/index')
    
  2. Token缓存策略

    // 合理的Token缓存时间
    setToken(token, { expires: 7 }) // 7天过期
    

7.2 用户体验优化

  1. 加载状态展示

    <div v-if="loading" class="loading-content"><i class="el-icon-loading"></i><p>正在验证登录信息,请稍候...</p>
    </div>
    
  2. 错误处理友好化

    catch (error) {this.error = error.message || '单点登录失败,请重新登录'this.showPortalButton = true  // 提供返回选项
    }
    

8. 测试与验证

8.1 功能测试用例

  1. 正常登录流程

    • 验证Token有效时的自动登录
    • 验证redirect参数的正确跳转
  2. 异常情况处理

    • Token无效时的错误提示
    • Token过期时的重新登录引导
  3. 权限验证

    • 不同角色用户的页面访问权限
    • 动态路由的正确生成

8.2 安全测试

  1. Token篡改测试
  2. 重放攻击测试
  3. 跨站脚本攻击(XSS)防护测试

9. 部署与运维

9.1 部署配置

  1. Nginx配置

    location /sso {try_files $uri $uri/ /index.html;
    }
    
  2. 环境变量配置

    // 不同环境的SSO配置
    const ssoConfig = {development: 'http://dev-auth.company.com',production: 'https://auth.company.com'
    }
    

9.2 监控告警

  1. SSO成功率监控
  2. Token失效率统计
  3. 登录异常告警

10. 总结与展望

10.1 改造效果

  1. 用户体验提升:实现了一次登录,全平台访问
  2. 安全性增强:统一身份认证,集中权限管理
  3. 维护成本降低:减少了重复的认证逻辑开发

10.2 技术收益

  1. 架构统一:建立了统一的前端认证标准
  2. 代码复用:SSO组件可在其他项目中复用
  3. 扩展性强:便于后续接入更多业务系统

10.3 后续优化方向

  1. 微前端集成:支持微前端架构下的SSO
  2. 移动端适配:扩展到移动端应用
  3. 多因子认证:增加短信、邮箱等多因子认证支持

10.4 最佳实践建议

  1. 统一标准:制定企业级SSO技术标准
  2. 安全第一:严格执行安全编码规范
  3. 用户体验:关注用户登录体验的每个细节
  4. 监控运维:建立完善的监控和告警机制
http://www.dtcms.com/a/357151.html

相关文章:

  • 关于锁相放大器(LIA)的系统论文研究(重点于FPGA部分)
  • 设计模式:装饰模式(Decorator Pattern)
  • iOS开发之苹果系统包含的所有字体库
  • 最小生成树——Kruskal
  • 【机器学习入门】3.1 关联分析——从“购物篮”到推荐系统的核心逻辑
  • 响应式编程框架Reactor【2】
  • Windows C盘完全占满会如何?
  • 2024-06-13-debian12安装Mariadb-Galera-Cluster+Nginx+Keepalived高可用多主集群
  • 毕马威 —— 公众对人工智能的信任、态度及使用情况调查
  • C++基础(②VS2022创建项目)
  • docker compose设置命令别名的方法
  • Windows WizTree-v4.27.0.0-x64[磁盘空间分析软件]
  • C++中类,this指针,构造函数,析构函数。拷贝构造函数,初步理解运算符重载,初步理解赋值运算符重载
  • 2.4G串口透传模组 XL2417D无线模块,实测通讯距离300m左右!
  • 第23章笔记|PowerShell 高级远程控制配置(端点、自定义、双向认证、多跳)
  • 常见视频编码格式对比
  • GraphRAG 知识图谱核心升级:集成 langextract 与 Gemini ----实现高精度实体与关系抽取
  • 捡捡java——2、基础05
  • Redis不同场景下的注意事项
  • 如何在FastAPI中玩转全链路追踪,让分布式系统故障无处遁形?
  • 【golang长途旅行第34站】网络编程
  • c++ template
  • Vue2+Element 初学
  • LRU 内存淘汰策略
  • 【51单片机定时1秒中断控制流水灯方向】2022-11-14
  • Geocodify 的 API
  • 以技术赋能强化消费者信任,助推餐饮服务质量提质增效的明厨亮灶开源了
  • 有鹿机器人:用智能清洁重塑多行业工作方式
  • Centos卸载anaconda
  • 微服务Eureka组件的介绍、安装、使用