SSO 与 JWT+Redis 的定位差异

SSO 与 JWT+Redis 的定位差异:Token格式、管理策略、认证架构三个层次 🤔 一、一个常见的学习困惑 很多开发者在学习鉴权体系时会遇到这样的困惑: 已经理解了 JWT 的三段式结构,知道它是无状态的 Token 格式 也理解了 JWT + Redis 混合方案,知道它能解决 Token 主动撤销的问题 然后听到"微服务用 SSO(单点登录)",去查资料后发现 SSO 也用 JWT 于是产生疑问:JWT + Redis 方案和 SSO 是什么关系?是不是同一个东西的不同叫法?如果不是,区别在哪? 这三个概念确实容易混淆,因为它们都围绕"鉴权"这个话题,但它们解决问题的层次完全不同。下面用三个明确的定义开篇: 概念 本质 解决什么问题 JWT Token 数据格式 Token 如何编码用户信息、如何防篡改 JWT + Redis Token 管理策略(单服务内部) 单个服务如何签发、验证、撤销 Token SSO(单点登录) 认证架构模式(跨服务) 多个服务之间如何共享登录状态 💼 二、从一个具体的业务场景理解差异 假设你所在的公司有三个系统: OA 办公系统(oa.company.com)—— 审批、考勤 CRM 客户系统(crm.company.com)—— 客户管理 BI 报表系统(bi.company.com)—— 数据分析 🏝️ 2.1 没有 SSO 时:每个系统各自鉴权 sequenceDiagram participant U as 用户 participant OA as OA系统 participant CRM as CRM系统 participant BI as BI系统 Note over U,BI: 用户需要分别登录3个系统 U->>OA: 打开OA → 输入用户名密码 OA-->>U: 登录成功 (OA的Token) U->>CRM: 打开CRM → 再次输入用户名密码 CRM-->>U: 登录成功 (CRM的Token) U->>BI: 打开BI → 第三次输入用户名密码 BI-->>U: 登录成功 (BI的Token) 每个系统都有自己独立的用户表、独立的登录接口、独立签发 Token。用户需要在三个系统之间各登录一次。这里的每个系统内部,可能各自使用了 JWT + Redis 管理自己的 Token——但这和"用户只需登录一次"是两个不同的问题。 ...

九月 21, 2022 · 9 分钟 · 1755 字 · yaomingye

JWT 与双令牌机制详解

JWT 与双令牌机制详解:从结构原理到 Java 代码实现 🤔 一、一个登录请求背后的困境 你写完了一个登录接口,用户提交用户名密码,服务端验证通过后创建 Session,把用户信息存进去,返回一个 JSESSIONID 的 Cookie。后续请求自动带上这个 Cookie,服务端从 Session 中取出用户信息——这是最传统的 Session 认证方式。 @PostMapping("/login") public String login(HttpSession session, @RequestBody LoginRequest req) { User user = userService.verify(req.getUsername(), req.getPassword()); if (user == null) { return "用户名或密码错误"; } session.setAttribute("currentUser", user); // 存入Session return "登录成功"; } @GetMapping("/info") public User info(HttpSession session) { return (User) session.getAttribute("currentUser"); // 从Session取 } 这段代码在单机部署时没有问题。但当你部署到 3 台服务器、前面挂了一个 Nginx 负载均衡时,问题就出现了: ...

九月 20, 2022 · 12 分钟 · 2361 字 · yaomingye

JWT + Redis 双令牌鉴权实战

JWT + Redis 双令牌鉴权实战:生产环境下的 Token 主动失效与过期管理方案 🔥 一、一个真实的生产事故 先看一段在中小项目中常见的鉴权代码: // 登录:生成JWT,返回给客户端 public String login(String username, String password) { User user = userService.verify(username, password); return Jwts.builder() .setSubject(user.getId().toString()) .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000)) .signWith(SECRET_KEY) .compact(); } // 拦截器:验证JWT签名和过期时间 public boolean preHandle(HttpServletRequest request, ...) { String token = request.getHeader("Authorization"); Claims claims = Jwts.parserBuilder() .setSigningKey(SECRET_KEY).build() .parseClaimsJws(token).getBody(); // Token签名正确且未过期 → 放行 return true; } 这段代码能跑吗?能。有安全隐患吗?有,而且很严重。 ...

九月 19, 2022 · 19 分钟 · 3916 字 · yaomingye

Spring Security + JWT 企业级鉴权实战

Spring Security + JWT 企业级鉴权实战:从零概念到完整代码实现 阅读前提:本文假设你已经会使用 Spring Boot 写基本的 CRUD 接口。如果你从未接触过 Spring Security,从这篇开始即可。 本文按照"先搞懂概念 → 教程版完整实现 → 生产版逐项升级 → 验证排错“的顺序组织。如果你只想快速跑通一个能用的版本,读完 Part 1 后直接看 Part 2 即可;如果你想理解企业级项目的真实做法,需要完整读完。 Part 1:先搞懂要做什么 在写任何代码之前,先把三个问题搞清楚:认证和授权到底是什么?Spring Security 怎么运作的?JWT 是什么? 一、从一个没有防护的接口说起 假设你用 Spring Boot 写了一个用户管理接口: @RestController @RequestMapping("/api/admin") public class AdminController { @GetMapping("/users") public List<User> listAllUsers() { // 返回系统中所有用户信息 return userService.findAll(); } @DeleteMapping("/users/{id}") public String deleteUser(@PathVariable Long id) { userService.deleteById(id); return "删除成功"; } } 启动项目后,任何人只要知道 URL,就能直接访问这些接口——不需要登录,不需要权限。这在企业生产环境中是不可接受的。 ...

九月 18, 2022 · 23 分钟 · 4761 字 · yaomingye

Spring Boot企业开发高频注解完全指南

Spring Boot企业开发高频注解完全指南:从IoC容器到数据访问全覆盖 🤔 一、为什么需要这份注解清单 初学 Spring Boot 时,打开官方文档会看到上百个注解。但实际企业开发中,真正高频使用的注解只有其中一部分。很多注解你可能工作三五年也用不到一次。 本文筛选出企业开发中使用频率最高的 Spring 注解(不含 SpringMVC 和 SpringSecurity),每个注解都配有可运行的示例代码和一句话说明它的用途。不解释底层原理,只告诉你"这是什么、怎么用、什么时候用"。 注解来源范围:Spring Framework + Spring Boot + Spring Data JPA + Spring AOP + Spring Cache + Spring Scheduling + Spring Retry。 约定:下文所有示例均基于 Spring Boot 项目,包路径省略。示例中 @Service、@Repository 等注解未重复展示之处,默认已配合 @ComponentScan 自动扫描。 🗺️ 二、注解分类全景图 在实际进入每个注解之前,先用一张分类图建立全局认知: flowchart LR classDef root fill:#0f172a,stroke:#3b82f6,stroke-width:2px,color:#bfdbfe,font-weight:bold; classDef branch fill:#2d1a05,stroke:#f59e0b,stroke-width:2px,color:#fde68a,font-weight:bold; classDef leaf fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; ROOT[Spring注解体系] ROOT --> B1[1.IoC容器核心] ROOT --> B2[2.Boot启动配置] ROOT --> B3[3.AOP切面] ROOT --> B4[4.事务管理] ROOT --> B5[5.异步与定时] ROOT --> B6[6.缓存管理] ROOT --> B7[7.数据校验] ROOT --> B8[8.JPA数据访问] ROOT --> B9[9.事件监听] ROOT --> B10[10.测试支持] ROOT --> B11[11.条件装配] ROOT --> B12[12.重试机制] class ROOT root; class B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12 branch; 📦 三、Spring IoC 容器核心注解 IoC(控制反转)和 DI(依赖注入)是 Spring 的根基。以下是日常开发中必用的注解。 ...

九月 17, 2022 · 17 分钟 · 3415 字 · yaomingye

NIO 性能调优

NIO 性能调优:零拷贝、直接内存与线上问题排查全解析 1 ⚡ 问题切入:文件服务器 CPU 100%,网络带宽却没用满 一个典型的文件下载服务,使用传统 Java I/O 实现: // 传统文件传输:将磁盘文件发送给客户端 public static void sendFile(Socket socket, String filepath) throws IOException { FileInputStream fis = new FileInputStream(filepath); BufferedInputStream bis = new BufferedInputStream(fis); OutputStream os = socket.getOutputStream(); byte[] buf = new byte[8192]; int len; while ((len = bis.read(buf)) != -1) { os.write(buf, 0, len); // 每次循环:内核→用户→内核→网卡 } bis.close(); fis.close(); } 这段代码能工作,但投入生产后出现异常现象:4 核 CPU 全部 100%,但千兆网卡只用了 600Mbps。理论上这台机器完全可以跑满千兆,为什么 CPU 先成了瓶颈? ...

九月 16, 2022 · 9 分钟 · 1755 字 · yaomingye

Netty

Netty:Reactor 线程模型、Pipeline 责任链与四个必写示例全解析 1 ⚡ 问题切入:原生 NIO 能工作,但你敢上生产吗? 在上一篇 Java NIO 博客中,我们手写了一个 NIO EchoServer——单线程管理多个连接,Selector 封装 epoll。这段代码在演示环境中运行良好,但如果直接部署到生产环境,会遇到四个棘手问题: // 原生 NIO EchoServer 的核心循环(看似正确,实则隐患重重) while (true) { selector.select(); for (SelectionKey key : selector.selectedKeys()) { if (key.isAcceptable()) { SocketChannel client = ssc.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buf = ByteBuffer.allocate(1024); int len = client.read(buf); // 问题1: 读到半包怎么办? buf.flip(); // 问题2: 忘了 flip 直接炸 client.write(buf); // 问题3: 写不出去谁管? buf.clear(); // 问题4: clear 还是 compact? } keyIterator.remove(); } } 原生 NIO 的四大生产痛点: ...

九月 15, 2022 · 13 分钟 · 2657 字 · yaomingye

Java NIO

Java NIO:epoll 多路复用、Buffer 机制与单线程高并发全解析 1 ⚡ 问题切入:一个线程如何管理 10000 个连接? 在经典的 BIO (Blocking I/O,阻塞 I/O)模型下,每个 Socket 连接需要分配一个独立线程。当 accept() 返回一个新连接,就启动一个线程去 read() —— 这个线程在数据到达之前会一直阻塞,CPU 时间被白白浪费在线程上下文切换上。 // 传统 BIO:一个连接一个线程(不可行) ServerSocket server = new ServerSocket(8080); while (true) { Socket client = server.accept(); // 阻塞等待连接 new Thread(() -> { InputStream in = client.getInputStream(); byte[] buf = new byte[1024]; in.read(buf); // 阻塞等待数据 // 处理数据... }).start(); } 问题:如果有 10,000 个连接,就需要 10,000 个线程。每个 Java 线程默认栈大小约 1MB,仅线程栈就消耗 10GB 内存,而且 CPU 绝大多数时间都在做线程切换而非真正处理数据。这就是著名的 C10K 问题 (Client 10,000 Problem)。 ...

九月 14, 2022 · 17 分钟 · 3496 字 · yaomingye

Linux IO 模型

Linux IO 模型:阻塞、非阻塞、多路复用与异步 IO 全解析 1 ⚡ 问题切入:一个后端开发者必须回答的问题 假设你在面试中被问到: “一台 4 核 8GB 的服务器,为什么能支撑 10 万个并发连接?” 答案的关键不在于 CPU 有多快、内存有多大,而在于 IO 模型 。如果每个连接用一个线程、每个线程做阻塞 IO,10 万连接就需要 10 万个线程——每个线程消耗约 1MB 栈空间,仅线程栈就占 100GB 内存,4 核 CPU 也根本无法调度这么多线程。 真正让高并发成为可能的,是 非阻塞 IO 和 IO 多路复用 (I/O Multiplexing,单个线程同时监听多个 IO 事件)。Nginx、Redis、Netty 的高性能都建立在正确的 IO 模型选择之上。 这篇博客从操作系统层面讲解 Linux 五大 IO 模型,聚焦于"数据如何从网卡/磁盘到达你的程序",为后续理解 Java NIO、Netty、Kafka 等框架打下理论基础。 2 💻 硬件架构:一次 IO 操作经历了什么 在讨论 IO 模型之前,必须先理解一次 IO 操作涉及哪些硬件组件以及数据如何流转。 如上图所示,一次典型的网络 IO 读取,数据经过以下路径: ...

九月 13, 2022 · 9 分钟 · 1736 字 · yaomingye

RandomAccessFile 文件指针与任意位置读写

RandomAccessFile 文件指针与任意位置读写:多线程分段下载实现 1 ⚡ 问题切入:流式读写的局限 前面介绍的 FileInputStream / FileOutputStream 都是 顺序流 (Sequential Stream),只能从文件头开始,一个字节接一个字节地读写。如果只想读文件的第 1000 个字节,顺序流必须先把前面 999 个字节读完(或调用 skip()),效率很低。 如果要实现以下场景,顺序流就完全不够用了: 修改一个 1GB 文件的中间某几个字节 在文件末尾追加日志,同时读取文件头部的元数据 多线程分段下载文件,每个线程负责不同区间的数据块 这些场景需要 随机读写 ——可以自由移动文件指针到任意位置,像操作数组下标一样操作文件。Java 提供了 RandomAccessFile 来实现这个能力。 // 随机读:直接跳到第 1000 字节开始读 RandomAccessFile raf = new RandomAccessFile("data.bin", "r"); raf.seek(1000); // 指针移到 position=1000 int b = raf.read(); // 读第 1000 个字节 System.out.printf("第1000字节: 0x%02X%n", b); raf.close(); 跳过前面 999 个字节,直达目标位置——这就是 RandomAccessFile 的价值。 2 🎯 核心概念:文件指针 2.1 ❓ 什么是文件指针 文件指针 (File Pointer)是 RandomAccessFile 内部维护的一个 long 类型偏移量(Offset),表示 下一次读写操作将发生的字节位置 。它类似于数组的下标索引,但作用对象是文件。 ...

九月 12, 2022 · 8 分钟 · 1611 字 · yaomingye
Cat Radio