基于Spring Session + Redis + JWT的单点登录实现

实现思路

  1. 用户访问受保护资源时,若未认证则重定向到认证中心
  2. 认证中心验证用户身份,生成JWT令牌并存储到Redis
  3. 认证中心重定向回原应用并携带令牌
  4. 应用验证JWT有效性并从Redis获取会话信息
  5. 用户在其他应用访问时,通过相同机制实现单点登录

代码实现

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>SSO 单点登录演示</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"><style>.system-card {transition: all 0.3s ease;cursor: pointer;}.system-card:hover {transform: translateY(-5px);box-shadow: 0 10px 20px rgba(0,0,0,0.1);}.logged-in {border-left: 4px solid #198754;}.not-logged-in {border-left: 4px solid #dc3545;}.status-indicator {width: 12px;height: 12px;border-radius: 50%;display: inline-block;margin-right: 5px;}.online {background-color: #198754;}.offline {background-color: #6c757d;}</style>
</head>
<body><div class="container my-5"><div class="row mb-4"><div class="col text-center"><h1 class="display-4">单点登录(SSO)演示系统</h1><p class="lead">基于 Spring Session + Redis + JWT 实现</p><div class="mt-4" id="user-info" style="display: none;"><p>当前用户: <span id="username" class="fw-bold">未登录</span></p><button id="logout-btn" class="btn btn-danger btn-sm">退出登录</button></div></div></div><div class="row mb-4"><div class="col-md-8 mx-auto"><div class="card"><div class="card-header bg-primary text-white"><h5 class="mb-0">登录认证中心</h5></div><div class="card-body"><div id="login-form"><div class="mb-3"><label for="inputUsername" class="form-label">用户名</label><input type="text" class="form-control" id="inputUsername" value="admin"></div><div class="mb-3"><label for="inputPassword" class="form-label">密码</label><input type="password" class="form-control" id="inputPassword" value="password"></div><button id="login-btn" class="btn btn-primary">登录</button></div></div></div></div></div><div class="row"><h3 class="mb-4">应用系统</h3><div class="col-md-4 mb-4"><div class="card system-card not-logged-in"><div class="card-body"><h5 class="card-title">人力资源系统</h5><p class="card-text">访问员工信息、薪资数据等</p><div class="d-flex justify-content-between align-items-center"><span><span class="status-indicator online"></span>运行中</span><span class="badge bg-secondary" id="hr-status">未登录</span></div></div></div></div><div class="col-md-4 mb-4"><div class="card system-card not-logged-in"><div class="card-body"><h5 class="card-title">客户关系管理</h5><p class="card-text">管理客户信息和销售渠道</p><div class="d-flex justify-content-between align-items-center"><span><span class="status-indicator online"></span>运行中</span><span class="badge bg-secondary" id="crm-status">未登录</span></div></div></div></div><div class="col-md-4 mb-4"><div class="card system-card not-logged-in"><div class="card-body"><h5 class="card-title">财务系统</h5><p class="card-text">处理财务数据和报表</p><div class="d-flex justify-content-between align-items-center"><span><span class="status-indicator online"></span>运行中</span><span class="badge bg-secondary" id="finance-status">未登录</span></div></div></div></div></div><div class="row mt-5"><div class="col"><div class="card"><div class="card-header bg-info text-white"><h5 class="mb-0">SSO 流程说明</h5></div><div class="card-body"><ol><li>用户在任意系统访问受保护资源</li><li>系统检查本地会话,若无有效会话则重定向到认证中心</li><li>认证中心检查全局会话,若已登录则直接颁发令牌</li><li>若未登录,显示登录页面,用户提交凭证</li><li>认证中心验证凭证,创建全局会话,生成JWT令牌</li><li>认证中心重定向回原系统并携带令牌</li><li>原系统验证JWT有效性,创建本地会话</li><li>用户访问其他系统时,重复上述流程实现单点登录</li></ol></div></div></div></div></div><script>document.addEventListener('DOMContentLoaded', function() {const loginBtn = document.getElementById('login-btn');const logoutBtn = document.getElementById('logout-btn');const userInfoDiv = document.getElementById('user-info');const systems = [{ id: 'hr-status', name: '人力资源系统' },{ id: 'crm-status', name: '客户关系管理' },{ id: 'finance-status', name: '财务系统' }];// 模拟登录功能loginBtn.addEventListener('click', function() {const username = document.getElementById('inputUsername').value;const password = document.getElementById('inputPassword').value;// 模拟认证过程if (username && password) {// 显示用户信息document.getElementById('username').textContent = username;userInfoDiv.style.display = 'block';// 更新系统状态systems.forEach(system => {const element = document.getElementById(system.id);element.className = 'badge bg-success';element.textContent = '已登录';// 更新卡片样式const card = element.closest('.system-card');card.classList.remove('not-logged-in');card.classList.add('logged-in');});// 模拟JWT令牌生成和存储const mockJwt = generateMockJWT(username);localStorage.setItem('sso_token', mockJwt);alert('登录成功!已生成JWT令牌并存储在Redis中。');} else {alert('请输入用户名和密码');}});// 模拟退出功能logoutBtn.addEventListener('click', function() {// 清除用户信息userInfoDiv.style.display = 'none';// 更新系统状态systems.forEach(system => {const element = document.getElementById(system.id);element.className = 'badge bg-secondary';element.textContent = '未登录';// 更新卡片样式const card = element.closest('.system-card');card.classList.remove('logged-in');card.classList.add('not-logged-in');});// 清除本地存储的令牌localStorage.removeItem('sso_token');alert('已退出登录,全局会话已清除。');});// 模拟点击系统卡片document.querySelectorAll('.system-card').forEach(card => {card.addEventListener('click', function() {const statusBadge = this.querySelector('.badge');if (statusBadge.textContent === '未登录') {// 检查是否有全局会话const token = localStorage.getItem('sso_token');if (token) {// 有全局会话,直接登录该系统statusBadge.className = 'badge bg-success';statusBadge.textContent = '已登录';this.classList.remove('not-logged-in');this.classList.add('logged-in');alert('单点登录成功!使用Redis中的会话信息自动登录。');} else {// 无全局会话,跳转到认证中心alert('未登录,跳转到认证中心...');// 实际场景中这里会重定向到认证中心}} else {alert('访问 ' + this.querySelector('.card-title').textContent);}});});// 模拟生成JWT令牌的函数function generateMockJWT(username) {// 实际JWT包含header.payload.signature三部分// 这里仅做演示,生成一个模拟的令牌const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));const payload = btoa(JSON.stringify({ sub: username, iat: Date.now(), exp: Date.now() + 3600000 // 1小时后过期}));const signature = 'mock-signature'; // 实际场景中会用密钥生成签名return `${header}.${payload}.${signature}`;}});</script>
</body>
</html>

技术实现说明

1. 核心组件

  • Spring Session: 用于统一会话管理,将HTTP会话存储到Redis中
  • Redis: 作为集中式会话存储,支持多个应用共享会话数据
  • JWT: 作为身份验证令牌,在应用间安全传递用户身份信息

2. 关键流程

  1. 用户访问应用系统:系统检查本地会话是否存在
  2. 未认证重定向:若无有效会话,重定向到认证中心并携带回调地址
  3. 认证中心检查:认证中心检查是否存在全局会话
  4. 用户登录:若无全局会话,用户提交凭证进行认证
  5. 创建会话:认证成功后,创建全局会话并存储到Redis
  6. 颁发令牌:生成JWT并重定向回原应用
  7. 验证令牌:原应用验证JWT有效性,创建本地会话
  8. 单点访问:用户访问其他应用时,重复上述流程实现单点登录

3. 后端实现要点(伪代码)

// 认证中心控制器
@Controller
public class AuthController {@PostMapping("/login")public String login(String username, String password, String redirectUrl, HttpSession session, Model model) {// 验证用户凭证User user = userService.authenticate(username, password);if (user != null) {// 创建全局会话session.setAttribute("user", user);// 生成JWT令牌String token = JwtUtil.generateToken(user);// 存储令牌与会话的关联到RedisredisTemplate.opsForValue().set("sso:" + token, session.getId());// 重定向回原系统return "redirect:" + redirectUrl + "?token=" + token;}return "login";}
}// 应用系统拦截器
public class SsoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);// 检查本地会话if (session != null && session.getAttribute("user") != null) {return true;}// 检查请求中的JWT令牌String token = request.getParameter("token");if (token != null && JwtUtil.validateToken(token)) {// 从Redis获取会话IDString sessionId = redisTemplate.opsForValue().get("sso:" + token);if (sessionId != null) {// 创建本地会话HttpSession newSession = request.getSession();newSession.setAttribute("user", getUserFromToken(token));return true;}}// 重定向到认证中心response.sendRedirect(authCenterUrl + "?redirectUrl=" + currentUrl);return false;}
}

扩展建议

  1. 安全性增强:使用HTTPS传输,添加CSRF保护,设置合理的JWT过期时间
  2. 性能优化:使用Redis集群提高会话存储性能,添加本地会话缓存
  3. 用户体验:实现无缝跳转,提供统一的登录/登出界面
  4. 监控管理:添加会话监控和管理功能,支持强制下线

这个演示展示了SSO的基本流程和实现方式,实际项目中需要根据具体需求进行调整和完善。

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

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

相关文章

微服务Eureka组件的介绍、安装、使用

微服务 Eureka 组件的介绍、安装与使用详解 在微服务架构中&#xff0c;服务注册与发现是至关重要的一环&#xff0c;而 Eureka 作为 Netflix 开源的服务注册中心&#xff0c;广泛应用于 Spring Cloud 微服务体系中。本文将带你全面了解 Eureka 的概念、安装及在 Spring Boot …

【PostgreSQL内核学习:通过 ExprState 提升哈希聚合与子计划执行效率(一)】

PostgreSQL内核学习&#xff1a;通过 ExprState 提升哈希聚合与子计划执行效率&#xff08;一&#xff09;引言背景补丁的意义补丁概述JIT & LLVM实际例子&#xff08;以 PostgreSQL 为例&#xff09;提交信息提交描述引入 ExprState 进行哈希计算&#xff1a;支持 JIT 编译…

web端播放flv视频流demo(flv.js的使用)

需求&#xff1a;原本是需要前端播放RTMP视频流的&#xff0c;但是现在的浏览器都不支持flash插件了&#xff0c;让用户安装flash插件也不现实&#xff0c;所以直接让后端将RTMP视频流转换成flv视频流给到前端进行播放。 直接上demo&#xff0c;直接就能播放&#xff0c;如果遇…

【拍摄学习记录】04-拍摄模式/曝光组合

曝光组合全自动半自动光圈优先手动挡【固定物体长时间不变时候、闪光灯时候、】手机上的光学变焦与数码变焦是不同的&#xff0c;使用档位推荐可以提升画质。手机夜景模式长曝光【车流轨迹、星轨】HDR 大光比【日落时候使用】专业模式&#xff0c;【感光度iso、快门可以调节】…

新liunx部署mysql过程问题

首先看下是什么发行版 cat /etc/os-release CentOS Linux 7 ################################## 使用 yum下载包 发现不行 源不行 那就换成阿里的 # 进入 yum 源配置目录 cd /etc/yum.repos.d/ # 备份所有默认 repo 文件&#xff08;以 CentOS 为例&#xff0c;其他系统…

Python 第三方库:Beautiful Soup(HTML/XML 解释提取)

Beautiful Soup 是一个 用于从 HTML 和 XML 文件中提取数据的 Python 第三方库。它为复杂的网页结构提供了简单易用的解析接口&#xff0c;尤其适合网页爬虫和数据提取任务。Beautiful Soup 提供树型结构访问、标签搜索、属性提取等功能&#xff0c;并支持多种解析器&#xff0…

使用STM32CubeMX使用CAN驱动无刷电机DJI3508

简介 文章为笔记性质 硬件包括 大疆C板 电机调速器C620 DJI3508电机 CAN知识介绍 CAN的概念 CAN是控制器区域网络&#xff08;Controller Area Network&#xff09;的缩写。CAN总线是一种适用于工业设备的高性能总线网络。说白了就是也就是一种通讯方式而已。 把多个设…

Wi-Fi 802.11s自组网/EasyMesh自组网/802.11ah物联网

一、前期调研结论 前面详细探讨了自组网和5G无线通信网络、WiFi无线通信网络的差异&#xff1a; 自组网 v.s 5G v.s WiFi-CSDN博客 从“分级道路”角度理解无线通信网络拓扑包括从当前工业应用场景具体案例了解终端无线通信网络&#xff1a; 5G无线通信网络场景&#xff08;…

【基于hyperledger fabric的教育证书管理系统】

教育证书管理系统 系统概述 项目背景 随着数字化转型的深入推进&#xff0c;教育证书作为个人学术成就和专业资质的重要凭证&#xff0c;在就业市场、高等教育和职业发展中扮演着关键角色。然而&#xff0c;传统教育证书管理体系面临着数据孤岛、证书伪造、验证流程繁琐以及跨机…

【Flask】测试平台开发,集成禅道

概述&#xff1a; 由于公司多数测试人员还是在使用禅道&#xff0c;为了方便&#xff0c;就将禅道直接集成在我们的测试平台中 一般可以有几种实现方法 调用禅道的API集成集成本地部署的禅道-可能有跨域问题&#xff0c;需要解决 由于我这里已经部署了一台本地的禅道系统&…

《UE5_C++多人TPS完整教程》学习笔记45 ——《P46 待机与跳跃(Idle And Jumps)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P46 待机与跳跃&#xff08;Idle And Jumps&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; St…

用html+js下拉菜单的demo,当鼠标点击后展开,鼠标点击别的地方后折叠

使用html js实现下拉菜单demo&#xff0c;因为copy的网站菜单功能失效&#xff0c;就需要自己写一个逻辑&#xff0c;点击其他区域折叠菜单&#xff0c;可以参考&#xff1a;<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF…

OpenCV 核心技术:颜色检测与几何变换实战

在计算机视觉任务中&#xff0c;颜色空间转换和图像几何变换是两大基础且高频的操作 —— 前者用于精准分割特定颜色目标&#xff08;如交通信号灯、物体追踪&#xff09;&#xff0c;后者用于调整图像的尺寸、位置和视角&#xff08;如文档矫正、图像拼接&#xff09;。本文将…

[HFCTF2020]EasyLogin

文章目录TRYWP总结TRY 注册admin报错username wrong。 随便注册一个用户点击GetFlag&#xff0c;permission deny。 猜测可能是需要admin权限。 看cookie发现有&#xff1a; sses:aok&#xff1a;eyJ1c2VybmFtZSI6ImEiLCJfZXhwaXJlIjoxNzU2NDU1NjczMTAxLCJfbWF4QWdlIjo4NjQwM…

Java接口和抽象类的区别,并举例说明

Java接口和抽象类是面向对象编程中实现抽象的两种机制&#xff0c;它们在语法、设计目的和使用场景上有显著区别&#xff1a;一、核心区别‌定义方式‌抽象类&#xff1a;使用abstract class声明&#xff0c;可包含抽象方法和具体方法45。接口&#xff1a;使用interface声明&am…

docker-相关笔记

1: 导入镜像 docker load -i myimage.tar# 导出镜像 docker save myimage:latest > myimage.tar # 导入镜像 docker load -i myimage.tardocker load -i <文件> 功能&#xff1a;用于导入通过 docker save 命令导出的镜像归档文件&#xff08;通常是 .tar 格式&#…

自然语言提取PDF表格数据

自然语言提取PDF表格数据PDF v8.2的文档解决方案与OpenAI实现了无缝的AI集成&#xff0c;可将非结构化PDF转换为可用数据。MESCIUS 推出的 PDF 文档解决方案 (DsPdf) 是一款软件开发工具包&#xff0c;它提供了 .NET Core 库和一个 JavaScript PDF 查看器&#xff0c;用于处理和…

飞牛Nas每天定时加密数据备份到网盘,基于restic的Backrest笔记分享

1. 前言 受前辈“RAID≠备份”的经验&#xff0c;也考虑到硬盘故障时 RAID 重建步骤繁琐&#xff0c;我干脆放弃阵列&#xff0c;直接单盘运行。 重要数据则加密后上传至大厂云盘&#xff1a;一方面文件对外不可读&#xff0c;规避扫描和谐&#xff1b;另一方面依靠大厂的数据安…

C#连接SQL-Server数据库超详细讲解以及防SQL注入

C#连接SQL Server数据库完整指南&#xff0c;整合了ADO.NET原生连接与Entity Framework Core两种实现方式。这篇文件详细介绍C#代码连接数据库的通用操作数据库链接功能 数据库的增删改查操作1 配置全局数据库链接字符串 App.config2 获取数据库链接字符串先在App.config配置连…

Pico2‑ICE FPGA 开发板:从开箱到跑通示例的全历程

FPGA 和 MCU 结合的开发板不多&#xff0c;而 Pico2‑ICE 则把小巧、灵活和易上手完美结合。搭载 RP2350 双核 RISC-V MCU Lattice iCE40UP5K FPGA&#xff0c;配合官方 SDK&#xff0c;你可以一步步跑通各种示例&#xff0c;从 LED 到 VGA&#xff0c;再到 MCU 与 FPGA 协作应…