【springboot+vue项目(十五)】基于Oauth2的SSO单点登录(二)vue-element-admin框架改造整合Oauth2.0

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架,提供了丰富的组件和功能,可以帮助开发者快速搭建现代化的后台管理系统。

一、基本知识

(一)Vue-element-admin 的主要文件和目录

vue-element-admin/
  |-- build/                          # 构建相关配置文件
  |    |-- build.js                   # 生产环境构建脚本
  |    |-- check-versions.js          # 检查 Node.js 和 npm 版本的脚本
  |    |-- logo.png                   # 构建 Logo
  |    |-- utils.js                   # 构建工具函数
  |    |-- vue-loader.conf.js         # Vue loader 配置
  |    |-- webpack.base.conf.js       # webpack 基础配置
  |    |-- webpack.dev.conf.js        # webpack 开发环境配置
  |    |-- webpack.prod.conf.js       # webpack 生产环境配置
  |
  |-- config/                         # 项目配置
  |    |-- dev.env.js                 # 开发环境变量配置
  |    |-- index.js                   # 项目配置文件
  |    |-- prod.env.js                # 生产环境变量配置
  |
  |-- src/                            # 源代码
  |    |-- api/                       # 接口请求相关
  |    |-- assets/                    # 静态资源
  |    |-- components/                # 全局公用组件
  |    |-- directive/                 # 自定义指令
  |    |-- icons/                     # 图标
  |    |-- layout/                    # 全局布局
  |    |-- router/                    # 路由配置
  |    |-- store/                     # 全局状态管理
  |    |-- styles/                    # 全局样式
  |    |-- utils/                     # 工具函数
  |    |-- views/                     # 页面组件
  |    |-- App.vue                    # 入口页面
  |    |-- main.js                    # 入口 JS 文件

  |    |-- permission.js           # 路由守卫 文件
  |
  |-- static/                         # 静态资源
  |
  |-- .babelrc                        # Babel 配置
  |-- .editorconfig                   # 编辑器配置
  |-- .eslintignore                   # ESLint 忽略文件配置
  |-- .eslintrc.js                    # ESLint 配置
  |-- .gitignore                      # Git 忽略文件配置
  |-- index.html                      # 入口 HTML 文件
  |-- package.json                    # 项目信息和依赖配置
  |-- README.md                       # 项目说明文档
  |-- vue.config.js                   # Vue CLI 配置 

(二)单点登录系统涉及的文件和概念

  1. 客户端ID和客户端密钥:前端应用和后端应用在与单点登录系统通信时,需要提供客户端ID和客户端密钥进行身份验证。
  2. 授权码(authorization code):单点登录系统验证用户身份成功后生成的一次性授权码,用于换取访问令牌和刷新令牌。
  3. 访问令牌(access token):单点登录系统验证成功后返回给前端应用的令牌,用于后续请求时进行身份验证。
  4. 刷新令牌(refresh token):单点登录系统验证成功后返回给前端应用的令牌,用于在访问令牌过期时更新访问令牌。
  5. 前端应用使用的SDK或库文件:前端应用需要集成相应的SDK或库文件以便与单点登录系统进行通信。
  6. 单点登录系统的API文档:开发人员需要根据API文档了解单点登录系统提供的接口和参数,以便正确调用API接口。

二、 前端实现单点登录的基本流程

设计思路:为了避免code和access_token的泄露,所以大部分的和单点登录系统(统一认证)的交互都放到后端进行,前端尽可能的复用原来的代码,进行小的改动。整体思路如下:

1、登录页面--login.vue(修改原login.vue):用户访问前端应用,前端应用将用户重定向到后端登录接口。

<template><div class="login-container"><div>正在重定向到登录页面...</div></div>
</template><script>
export default {created() {const baseUrl = process.env.VUE_APP_BASE_API;//重定向到后端的SSOlogin/login接口window.location.href = `${baseUrl}/SSOlogin/login`;},
};
</script><style scoped>
.login-container {display: flex;justify-content: center;align-items: center;height: 100vh;text-align: center;
}
</style>

2、 响应页面--callback.vue(新建):检查后端返回的URL+token访问链接中是否存在后端返回的token,如果存在则调用登录函数user/login进行前端的登录操作(调用login函数进行前端的登录,目的是将token持久化和存储到VUEX中,尽可能少的改动原来的代码。),并根据登录结果进行不同的处理。如果没有获取到token,则输出错误信息并终止后续逻辑。

<template><div><h1>Loading...</h1></div>
</template><script>
export default {name: "Callback",data() {return {};},created() {// 获取 URL 查询参数中的授权码const token = this.$route.query.token;// 利用后端传递的token,调用login函数进行前端的登录,目的是将token持久化和存储到VUEX中,尽可能少的改动原来的代码if (token) {this.loading = true;this.$store.dispatch("user/login",token).then(() => {// 登录成功,路由跳转this.$router.push({ path: this.redirect || "/" });this.loading = false;}).catch(() => {this.loading = false;});} else {console.log("error submit!!");return false;}},
};
</script>

 对应的store/user.js。

3、 数据仓库 store/user.js,修改一下原来的Login函数即可。

原Login函数为:

//这里在处理登录业务async login({ commit }, userInfo) {//解构出用户名与密码const { username, password } = userInfo;let result = await login({ username: username.trim(), password: password });if(result.code==200){//vuex存储tokencommit('SET_TOKEN',result.data.token);//本地持久化存储tokensetToken(result.data.token);return 'ok';}else{return Promise.reject(new Error('faile'));}},

 修改后的Login为:

 // 登录 actionasync login({ commit }, token) {try {// 设置 token 持久化commit('SET_TOKEN', token);// 将 token 值保存在浏览器的本地存储中setToken(token);// 可以根据需要返回其他数据或状态return "ok";} catch (error) {// 异常时返回一个被拒绝的 Promise 对象return Promise.reject(error);}},

 所以,修改login函数,去掉了使用用户名和密码发送请求到后端接口验证登录,完整的代码如下:

// 引入需要使用的函数和模块
import { getInfo, logout  } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'// 定义获取默认状态的函数
const getDefaultState = () => {return {token: getToken(), // 使用 getToken 函数获取 token 值name: '',avatar: ''}
}// 定义 Vuex 模块的状态
const state = getDefaultState()// 定义 Vuex 模块的变更操作
const mutations = {// 重置状态为默认状态RESET_STATE: (state) => {Object.assign(state, getDefaultState())},// 设置 tokenSET_TOKEN: (state, token) => {state.token = token},// 设置用户名SET_NAME: (state, name) => {state.name = name},// 设置用户头像SET_AVATAR: (state, avatar) => {state.avatar = avatar}
}// 定义 Vuex 模块的异步操作
const actions = {// 用户登录// 登录 actionasync login({ commit }, token) {try {// 设置 tokencommit('SET_TOKEN', token);// 将 token 值保存在浏览器的本地存储中setToken(token);// 可以根据需要返回其他数据或状态return "ok";} catch (error) {// 异常时返回一个被拒绝的 Promise 对象return Promise.reject(error);}},async getInfo({ commit, state }) {try {// 发送请求获取用户信息const response = await getInfo(state.token)// 如果响应数据不存在,说明验证失败if (!response.data) {throw new Error('验证失败,请重新登录。')}// 获取用户名和头像const { name, avatar } = response.data// 设置用户名commit('SET_NAME', name)// 设置用户头像commit('SET_AVATAR', avatar)// 返回完整的响应对象return response} catch (error) {return Promise.reject(error)}},// 用户注销async logout({ commit, state }) {try {// 发送请求注销用户登录状态await logout(state.token)// 从本地存储中删除 tokenremoveToken()// 重置路由resetRouter()// 重置状态commit('RESET_STATE')} catch (error) {return Promise.reject(error)}},// 重置 token 值async resetToken({ commit }) {try {// 从本地存储中删除 tokenremoveToken()// 重置状态commit('RESET_STATE')} catch (error) {return Promise.reject(error)}}
}// 导出 Vuex 模块
export default {namespaced: true, // 开启命名空间state,mutations,actions
}

4、  数据仓库store/user.js引入的函数和模块

 为了更清晰的了解整个过程,将上面store/user.js引入的函数和模块也贴在下面即:

因为 store/user.js中不用再向后端发请求,所以api/user.js中就可以把login删掉了。

api/user.js 

//api/user.js
import request from '@/api/request/request'export function getInfo(token) {return request({url: '/user/getInfo',method: 'get',params: { token }})
}export function logout() {return request({url: '/user/logout',method: 'post'})
}

utils/auth.js:用于处理用户身份验证 token 的工具函数,主要涉及对浏览器 Cookie 的操作。getToken:获取用户的身份验证 token,setToken:设置用户的身份验证 token,removeToken:移除用户的身份验证 token。

//utils/auth.js
import Cookies from 'js-cookie'const TokenKey = 'vue_admin_template_token'export function getToken() {return Cookies.get(TokenKey)
}export function setToken(token) {return Cookies.set(TokenKey, token)
}export function removeToken() {return Cookies.remove(TokenKey)
}

5、路由守卫--permission.js:修改原来的)在每次路由导航之前进行身份验证和权限控制,确保用户在正确的身份状态下访问页面,并在页面切换后完成进度条的展示。

        如果用户已登录且获取了用户信息,则直接跳转到目标页面;

        如果用户未登录或获取用户信息失败,则跳转至登录页。

        同时,白名单内的页面可以在未登录状态下直接访问。

修改:配置白名单,把callback加进去

注意:通过 user/getInfo 获取用户信息  await  store.dispatch('user/getInfo'),所以api/user.js 里面要有getInfo函数。

 permission.js 完整的代码如下:

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // 引入进度条库
import 'nprogress/nprogress.css' // 引入进度条样式
import { getToken } from '@/utils/auth' // 引入获取 token 的方法
import getPageTitle from '@/utils/get-page-title' // 引入获取页面标题的方法NProgress.configure({ showSpinner: false }) // 配置进度条const whiteList = ['/login','/callback'] // 定义无需登录即可访问的白名单路由router.beforeEach(async(to, from, next) => {NProgress.start() // 开始进度条document.title = getPageTitle(to.meta.title) // 设置页面标题const hasToken = getToken() // 获取 tokenif (hasToken) {if (to.path === '/login') {// 如果已登录却访问登录页,则重定向到首页next({ path: '/' })NProgress.done() // 完成进度条} else {const hasGetUserInfo = store.getters.name // 判断是否已获取用户信息if (hasGetUserInfo) {// 如果已获取,则直接进入路由next()} else {try {// 如果未获取,则通过 user/getInfo 获取用户信息await store.dispatch('user/getInfo')console.log("获取用户信息")next()} catch (error) {// 如果获取用户信息失败,则重置 token 并跳转至登录页await store.dispatch('user/resetToken')Message.error(error || 'Has Error')next(`/login?redirect=${to.path}`)// window.location.href = `www.baidu.com`;NProgress.done()}}}} else {if (whiteList.indexOf(to.path) !== -1) {// 如果在白名单中,则直接进入路由next()} else {// 否则跳转至登录页next(`/login?redirect=${to.path}`)NProgress.done()}}
})router.afterEach(() => {NProgress.done() // 完成进度条
})

6、请求拦截器中添加token到请求头:和原来一样)在请求拦截器中,从cookie中获取token,并将其添加到请求的头信息中,这样可以确保每次请求都带上了token。

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'// 创建一个axios实例
const service = axios.create({baseURL: process.env.VUE_APP_BASE_API,  // 接口的基础路径timeout: 5000 // 请求超时时间
})// 请求拦截器
service.interceptors.request.use(config => {// 在请求发送前做一些处理if (store.getters.token) {// 如果有token就在请求头中加上tokenconfig.headers['token'] = getToken()}return config},error => {// 对请求错误做些什么console.log(error)return Promise.reject(error)}
)// 响应拦截器
service.interceptors.response.use(response => {// 对响应数据做一些处理,这里只返回响应数据中的data部分const res = response.data// 如果自定义的响应码不是20000,就判断为错误if (res.code !== 20000 && res.code !== 200) {// 在页面上显示错误信息Message({message: res.message || 'Error',type: 'error',duration: 5 * 1000})if (res.code === 50008 || res.code === 50012 || res.code === 50014) {// 重新登录MessageBox.confirm('您已经登出,您可以取消以留在此页面,或重新登录', '确认登出', {confirmButtonText: '重新登录',cancelButtonText: '取消',type: 'warning'}).then(() => {store.dispatch('user/resetToken').then(() => {location.reload()})})}// 返回一个被拒绝的Promise对象,用来表示错误return Promise.reject(new Error(res.message || 'Error'))} else {// 如果没有错误,就返回响应数据中的data部分return res}},error => {// 对响应错误做些什么console.log('err' + error)Message({message: error.message,type: 'error',duration: 5 * 1000})return Promise.reject(error)}
)export default service

7、获取用户信息并渲染页面:在需要展示用户信息的地方,从cookie中获取用户信息,并将其渲染到页面上。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/690618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何确定分库还是 分表?

分库分表 分库分表使用的场景不一样&#xff1a; 分表因为数据量比较大&#xff0c;导致事务执行缓慢&#xff1b;分库是因为单库的性能无法满足要求。 分片策略 1、垂直拆分 水平拆分 3 范围分片&#xff08;range&#xff09; 垂直水平拆分 4 如何解决数据查询问题&a…

【Jvm】性能调优(拓展)Jprofiler如何监控和解决死锁、内存泄露问题

文章目录 Jprofiler简介1.安装及IDEA集成Jprofiler2.如何监控并解决死锁3.如何监控及解决内存泄露(重点)4.总结5.后话 Jprofiler简介 Jprofilers是针对Java开发的性能分析工具(免费试用10天), 可以对Java程序的内存,CPU,线程,GC,锁等进行监控和分析, 1.安装及IDEA集成Jprofil…

车载软件架构 —— Adaptive AUTOSAR软件架构中时间同步、网络管理和软件更新策略

车载软件架构 —— Adaptive AUTOSAR软件架构中时间同步、网络管理和软件更新策略 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师&#xff08;Wechat&#xff1a;gongkenan2013&#xff09;。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成…

解决用IPV6+DDNS访问UNRAID webui周期性失效的问题,smb不能访问的问题

我使用的unraid系统使用ddns&#xff08;DDNSGO&#xff09;绑定域名&#xff08;阿里域名&#xff09;与主机的ipv6地址进行远程访问&#xff0c;unraid是6.12.8。 遇到的问题是&#xff0c;配置当时是没问题的&#xff0c;但是过几天就会失效&#xff0c;无法通过域名访问we…

Maven高级(一)

文章目录 Maven高级&#xff08;一&#xff09;1. 分模块设计与开发1.1 介绍1.2 实践1.2.1 分析1.2.2 实现 1.3 总结 2. 继承与聚合2.1 继承2.1.1 继承关系2.1.1.1 思路分析2.1.1.2 实现 2.1.2 版本锁定2.1.2.1 场景2.1.2.2 介绍2.1.2.3 实现2.1.2.4 属性配置 2.2 聚合2.2.1 介…

扫码即可快速协作:草料二维码底部协作面板功能详解

功能介绍 在二维码上添加 底部协作面板 功能后 &#xff0c;扫码后不仅可以阅读设备信息、产品资料等基本信息&#xff0c;还可以在二维码底部输入内容评论并他人快速协作&#xff0c;支持添加图文、语言、手写签名等操作。 底部协作面板是提供给组织内部成员快速协作的功能&…

《汇编语言》- 读书笔记 - 第10章-CALL 和 RET 指令

《汇编语言》- 读书笔记 - 第10章-CALL 和 RET 指令 10.1 ret 和 retf检测点 10.1 10.2 call 指令10.3 依据位移进行转移的 call 指令检测点 10.2 10.4 转移的目的地址在指令中的 call 指令检测点 10.3 10.5 转移地址在寄存器中的 call 指令10.6 转移地址在内存中的 call 指令检…

数据模型概念

一、概念 (1) 定义 在数据库系统中针对不同的使用对象和应用目的&#xff0c;采用不同的数据模型。根据模型的应用的不同目的&#xff0c;可以将这些模型划分为两类&#xff1a; (2) 分类 A&#xff1a;概念数据模型 它也称信息模型它是按用户的观点&#xff08;观念世界&…

JVM(2)实战篇

1 内存调优 1.1 内存溢出和内存泄漏 内存泄漏&#xff08;memory leak&#xff09;&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&#xff0c;这种情况就称之为内存泄漏。 内存泄漏绝…

单调队列和优先队列

本篇记录下一下关于单调队列和优先队列&#xff08;堆&#xff09;的方法以及解题思路. 文章目录 一. 单调队列1. 绝对差不超过限制得最长连续子数组2. 跳跃游戏 VI3. 设计自助结算系统4. 和至少为k的最短子数组5. 满足不等式的最大值 二. 优先队列1. 最后一块石头的重量2. 数据…

十二:枚举与注解

文章目录 01、枚举类的使用1.1、枚举类的理解1.2、自定义枚举类1.3、使用enum关键字定义枚举类1.4、Enum类中的常用方法1.5、使用enum关键字定义的枚举类实现接口 02、注解的使用2.1、注解的理解2.3、如何自定义注解2.4、jdk中4个基本的元注解的使用12.5、jdk中4个基本的元注解…

uniapp 适配鸿蒙next调研

1.官方的一些回答 DCloud有资源第一时间得到鸿蒙无apk手机的上市计划。我们和华为保持着紧密沟通&#xff0c;会把握好节奏&#xff0c;不用担心。大家可以观察一个信号&#xff0c;等微信的鸿蒙next版敲定了&#xff0c;鸿蒙无apk手机就可以明确上市计划了。鸿蒙的开发语言是a…

数据结构:动态内存分配+内存分区+宏+结构体

一、作业 1.定义一个学生结构体&#xff0c;包含结构体成员&#xff1a;身高&#xff0c;姓名&#xff0c;成绩&#xff1b;定义一个结构体数组有7个成员&#xff0c;要求终端输入结构体成员的值&#xff0c;根据学生成绩&#xff0c;进行冒泡排序。 #include <stdio.h>…

UE蓝图 Cast节点和源码

系列文章目录 UE蓝图 Cast节点和源码 文章目录 系列文章目录Cast节点功能一、Cast节点用法二、Cast节点使用场景三、Cast节点实现步骤四、Cast节点源码 Cast节点功能 在Unreal Engine&#xff08;UE&#xff09;中&#xff0c;Cast节点是一种蓝图系统中的节点&#xff0c;用于…

【性能测试入门必看】性能测试理论知识

一、性能测试理论知识 1、常用的七种性能测试方法 (1) 后端性能测试&#xff1a;其实&#xff0c;你平时听到的性能测试&#xff0c;大多数情况下指的是后端性能测试&#xff0c;也就是服务器端性能测试。后端性能测试&#xff0c;是通过性能测试工具模拟大量的并发用户请求&…

linux系统Grafana关联zabbix显示

Grafana关联zabbix 服务器下载浏览器配置开启zabbix插件配置zabbix数据源可视化Zabbix数据 服务器下载 grafana-cli plugins list-remote grafana-cli plugins list-remote|grep -i zabbix grafana-cli plugins install alexanderzobnin-zabbix-appsystemctl restart grafana-…

HBase 进阶

参考来源: B站尚硅谷HBase2.x 目录 Master 架构RegionServer 架构写流程MemStore Flush读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区&#xff08;自定义分区&#xff09;系统拆分 Master 架构 Master详细架构 1&#xff09;Meta 表格介…

Vue | (一)Vue核心(上) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue简介&#x1f4da;初识Vue&#x1f4da;模板语法&#x1f4da;数据绑定&#x1f4da;MVVM模型&#x1f4da;数据代理&#x1f407;回顾Object.defineproperty方法&#x1f407;何为数据代理&#x1f407;Vue中的数据代理 &#x1f4da;事件处理&#…

基于java的眼镜店仓库管理系统

源码获取&#xff0c;加V&#xff1a;qq2056908377 摘要&#xff1a; 随着电子商务的兴起&#xff0c;越来越多的商家选择在线销售他们的产品。眼镜店作为零售业的一种&#xff0c;也不例外。随着市场需求的不断增加&#xff0c;眼镜店需要更加高效的管理他们的仓库和库存&…

Mysql 权限与安全管理

0 引言 MySQL是一个多用户数据库&#xff0c;具有功能强大的访问控制系统&#xff0c;可以为不同用户指定允许的权限。MySQL用户可以分为普通用户和root用户。root用户是超级管理员&#xff0c;拥有所有权限&#xff0c;包括创建用户、删除用户和修改用户的密码等管理权限&…