【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.hqwc.cn/news/475111.html

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

相关文章

LabVIEW智能家居控制系统

LabVIEW智能家居控制系统 介绍了一个基于LabVIEW的智能家居控制系统的开发过程。该系统利用LabVIEW软件与硬件设备相结合&#xff0c;通过无线网络技术实现家居环境的实时监控与控制&#xff0c;提升居住舒适度和能源使用效率。 项目背景&#xff1a;随着科技的发展和生活水平…

《剑指Offer》笔记题解思路技巧优化 Java版本——新版leetcode_Part_4

《剑指Offer》笔记&题解&思路&技巧&优化_Part_4 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相识&#x1f622;&#x1f622;&#x1f622; 开始刷题1. LCR 148. 验证图书取出顺序——栈的压入、弹出序列2. LCR 14…

蓝桥杯官网填空题(寻找整数)

问题描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 有一个不超过 10^17 的正整数 n&#xff0c;知道这个数除以 2 至 49 后的余数如下表所示&#xff0c;求这个正整数最小是多少。 运行限制 最大运行时间&#xff1a;…

【个人博客搭建】butterfly主题配置

目录 一、基础配置 (一) 模板配置 1. 文章模板 2. 页面模板 (二) 创建页面和文章 1. 标签页【可选】 2. 分类页【可选】 3. 友链页【可选】 4. 404页面【可选】 5. 文章 (三) 导航栏设置 1. 基础项 2. 菜单项 (四) 页面设置 1. 代码框设置 2. 社交图标设置 3.…

实习日志14

完善条件查询和word生成列表 条件查询 word生成列表 1.阶段性总结 1.1.入职培训 首先&#xff0c;蔡老师的活字格软件开发入门和应用培训为我提供了深入了解软件开发的机会。通过学习&#xff0c;我掌握了一些基础的开发技能&#xff0c;尤其是在数据库设计和管理方面有了更深…

缩小ppt文件大小的办法

之前用别人模版做了个PPT&#xff0c;100多M,文件存在卡顿问题 解决办法&#xff1a; 1.找到ppt中哪个文件过大&#xff0c;针对解决 2.寻找视频/音频文件&#xff0c;减少体积 3.字体文件是不是过多的问题。 一、文件寻找的内容步骤&#xff1a; 步骤&#xff1a; 1.把p…

一、springBoot入门

一、springBoot入门 步骤一&#xff1a;分析 建立一个需求&#xff1a;使用 SpringBoot 开发一个web应用&#xff0c;浏览器发起请求 /hello后&#xff0c;给浏览器返回字符串“hello worid ~"。 构建步骤概况 创建Maven攻城导入spring-boot-stater-web起步依赖编写Cont…

中科大计网学习记录笔记(十二):TCP 套接字编程

前前言&#xff1a;大家看到这一章节的时候一定不要跳过&#xff0c;虽然标题是编程&#xff0c;但实际上是对 socket 的运行机制做了详细的讨论&#xff0c;对理解 TCP 有很大的帮助&#xff1b;但是由于本节涉及到了大量的编程知识&#xff0c;对于一些朋友来说不是很好理解&…

会声会影2024新功能及剪辑视频步骤教程

会声会影2024的新功能主要包括&#xff1a; 全新的标题动态与特效&#xff1a;用户可以为文字标题指定进入、中场和退出的不同动态效果&#xff0c;比如闪现进入、中场弹跳和淡出退出等&#xff0c;让文字标题更具动感。此外&#xff0c;还新增了多个标题特效&#xff0c;包括…

第207篇 | 新年新工作,一些职场的感悟

这是2024年一月份flomo和notion 上聚合的系列文章&#xff08;03&#xff09;&#xff1b; 具体方法用的是这个 &#xff1a; 【知识沙虫&#xff0c;一个简单易用的知识体系建模工具】https://mp.weixin.qq.com/s/V2Cdq-1PbMQYvpE4o9NLpQ 首先&#xff0c;方法用下来还是很给…

单片机学习笔记---AD/DA工作原理(含运算放大器的工作原理)

目录 AD/DA介绍 硬件电路模型 硬件电路 运算放大器 DA原理 T型电阻网络DA转换器 PWM型DA转换器 AD原理 逐次逼近型AD转换器 AD/DA性能指标 XPT2046 XPT2046时序 AD/DA介绍 AD&#xff08;Analog to Digital&#xff09;&#xff1a;模拟-数字转换&#xff0c;将模拟…

搭建游戏服务器需要高防御的服务器吗?

随着网络技术的不断发展&#xff0c;游戏行业也迎来了前所未有的发展机遇。然而随着游戏用户的不断增加&#xff0c;游戏服务器的安全问题也日益突出。一些攻击者可能会对游戏服务器进行攻击&#xff0c;例如DDoS攻击、CC攻击等&#xff0c;导致服务器无法正常运行&#xff0c;…