基于Oauth2的SSO单点登录---前端
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 配置
(二)单点登录系统涉及的文件和概念
- **客户端ID和客户端密钥:**前端应用和后端应用在与单点登录系统通信时,需要提供客户端ID和客户端密钥进行身份验证。
- **授权码(authorization code):**单点登录系统验证用户身份成功后生成的一次性授权码,用于换取访问令牌和刷新令牌。
- **访问令牌(access token):**单点登录系统验证成功后返回给前端应用的令牌,用于后续请求时进行身份验证。
- **刷新令牌(refresh token):**单点登录系统验证成功后返回给前端应用的令牌,用于在访问令牌过期时更新访问令牌。
- **前端应用使用的SDK或库文件:**前端应用需要集成相应的SDK或库文件以便与单点登录系统进行通信。
- **单点登录系统的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中获取用户信息,并将其渲染到页面上。
相关文章:

基于Oauth2的SSO单点登录---前端
Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架,提供了丰富的组件和功能,可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 (一)Vue-element-admin 的主要文件和目录 vue-element-admin/ |--…...
springboot 使用注解设置缓存时效
springboot 使用注解设置缓存时效 import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCach…...

QGIS二次开发(地图符号库操作)
实习三 地图符号库操作 3.1 任务要求 基于QGIS,实现地图符号的设计/存储与显示;基于QGIS实现一个点、线、面shp矢量图层文件的显示。通过设置引用的符号,改变矢量图层的显示效果;可编辑地图的符号库汇中的点符号、线符号、面符号…...

线性代数行列式
目录 二阶与三阶行列式 二元线性方程组与二阶行列式 三阶行列式 全排列和对换 排列及其逆序数 对换 n阶行列式的定义 行列式的性质 二阶与三阶行列式 二元线性方程组与二阶行列式 若是采用消元法解x1、x2的话则得到以下式子 有二阶行列式的规律可得:分…...
Vision Transformer (ViT) 论文的第二句话
Vision Transformer (ViT) 论文的第二句话 flyfish 原句: “In vision, attention is either applied in conjunction with convolutional networks, or used to replace certain components of convolutional networks while keeping their overall structure in…...

Github 2024-12-27 Java开源项目日报Top10
根据Github Trendings的统计,今日(2024-12-27统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目9Kotlin项目1C#项目1非开发语言项目1C++项目1《Hello 算法》:动画图解、一键运行的数据结构与算法教程 创建周期:476 天协议类型:Ot…...

气相色谱-质谱联用分析方法中的常用部件,分流平板更换
分流平板,是气相色谱-质谱联用分析方法中的一个常用部件,它可以实现气相色谱柱流与MS检测器流的分离和分流。常见的气质联用仪分流平板有很多种,如单层T型分流平板、双层T型分流平板、螺旋分流平板等等。 操作视频http://www.spcctech.com/v…...

centos7 免安装mysql5.7及配置(支持多个mysql)
一) 下载免安装包: mysql下载地址: https://dev.mysql.com/downloads/mysql/下载时,选择以前5.7版本: image 下载第一个TAR压缩包: image 二) 定义安装路径并解压安装包 1、假设需要把MySQL放到 /usr/local…...
Python的Pandas--Series的创建和实现
1.Series函数的格式: pandas.Series(data,index,dtype,name,copy) data:一组数据(ndarray类型、list、dict等类)或标量值 index:数据索引标签。如果不指定,默认为整数,从0开始 dtype&#x…...

OCR实践-问卷表格统计
前言 书接上文 OCR实践—PaddleOCROCR实践-Table-Transformer 本项目代码已开源 放在 Github上,欢迎参考使用,Star https://github.com/caibucai22/TableAnalysisTool 主要功能说明:对手动拍照的问卷图片进行统计分数(对应分数…...
uniapp中的条件编译
在script中 // #ifdef APP-PLUS console.log("11"); // #endif// #ifdef MP-WEIXIN console.log("22"); // #endif 在template中 <!-- #ifdef APP-PLUS --><view>哈哈哈</view> <!-- #endif --><!-- #ifdef MP-WEIXIN -->…...

Segment Routing Overview
大家觉得有意义和帮助记得及时关注和点赞!!! Segment Routing (SR) 是近年来网络领域的一项新技术,“segment” 在这里 指代网络隔离技术,例如 MPLS。如果快速回顾网络设计在过去几十年的 发展,我们会发现 SR 也许是正在形成的第三代网络设计…...

【K8s】专题十五(6):Kubernetes 网络之 Pod 网络调试
本文内容均来自个人笔记并重新梳理,如有错误欢迎指正! 如果对您有帮助,烦请点赞、关注、转发、订阅专栏! 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】(全…...

CMake 构建项目并整理头文件和库文件
本文将介绍如何使用 CMake 构建项目、编译生成库文件,并将头文件和库文件整理到统一的目录中以便在其他项目中使用。 1. 项目结构 假设我们正在构建一个名为 rttr 的开源库,初始的项目结构如下: D:\WorkCode\Demo\rttr-master\|- src\ …...

Boost之log日志使用
不讲理论,直接上在程序中可用代码: 一、引入Boost模块 开发环境:Visual Studio 2017 Boost库版本:1.68.0 安装方式:Nuget 安装命令: #只安装下面几个即可 Install-package boost -version 1.68.0 Install…...

多功能jquery图片预览放大镜插件
xZoom是一款多功能的jquery图片预览放大镜插件。它支持多种图片放大模式,可以和Fancy Box或Magnific Pop-up等插件结合使用,功能非常强大。 在线预览 下载 使用方法 在页面中引入jquery和xzoom.css以及xzoom.js文件。 <link rel"stylesheet&…...
CSS系列(39)-- Shapes详解
前端技术探索系列:CSS Shapes详解 ✨ 致读者:探索形状布局的艺术 👋 前端开发者们, 今天我们将深入探讨 CSS Shapes,这个强大的形状布局特性。 基础形状 🚀 圆形与椭圆 /* 基础圆形 */ .circle {widt…...
AI 神经网络在智能家居场景中的应用
在科技持续进步的当下,智能家居领域正经历着深刻变革,AI 神经网络技术的融入成为推动这一变革的关键力量,为家居生活带来了诸多显著变化与提升,本文将几种常见的AI算法应用做了一下总结,希望对物联网从业者有所帮助。 …...
Rocky DEM tutorial7_Conical Dryer_锥形干燥器
tutorial 7_Conical Dryer_锥形干燥器 文章目录 tutorial 7_Conical Dryer_锥形干燥器0. 目的1. 模型介绍2. 模型设置2.1设置physics2.2 导入几何2.3 设置motion2.4 Boundary边界设置2.5 设置材料2.6设置材料间相互作用2.7 创建粒子2.8 设置颗粒进口2.9 求解器设置3. 后处理Enj…...
CSS(二):美化网页元素
目录 字体样式 文本样式 列表样式 背景图片 字体样式 字体相关的 CSS 属性: font-family:设置字体font-size:设置字体大小font-weight:设置字体的粗细(如 normal, bold, lighter 等)color:…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...