单点登录到底是什么?

单点登录到底是什么?

CookieJWT ,从 SessionOAuth2

一篇 图解 + 实战完整入门手册


目录

  1. 导语:为什么要有 SSO
  2. 核心概念
  3. SSO 分类与对比
  4. Cookie-Based SSO 详解
  5. Token-Based SSO 详解
  6. OAuth2 + OpenID Connect 流程
  7. JWT 实战(Spring Boot + Redis)
  8. 常见安全问题 & 防护
  9. 总结:一张图看懂 SSO

导语:为什么要有 SSO?

没有 SSO 的场景:

  • 公司 5 个系统 → 用户记 5 套账号 → IT 部门天天重置密码
  • 员工离职 → 5 个系统分别禁用账号 → 漏一个 = 安全风险

单点登录(SSO)一次登录,全网通行一次退出,全网失效


核心概念

概念 说明
CAS Central Authentication Service(中央认证服务)
Token 登录后颁发的 凭证(JWT、Ticket)
TGT Ticket Granting Ticket(CAS 术语)
ST Service Ticket(CAS 术语)
IdP Identity Provider(身份提供者)
SP Service Provider(服务提供者)

SSO 分类与对比

类型 存储 代表协议 适用场景
Cookie-Based 浏览器 Cookie CAS 2.0/3.0 同域名
Token-Based Header/LocalStorage JWT/OIDC 跨域、前后端分离
OAuth2 Access Token OAuth2 + OIDC 第三方登录

1. 架构图(同域名)

2. 关键点

  • Cookie Domain.example.com → 二级域名共享
  • Token 验证 :各系统向 SSO 中心 校验(Redis/JWT)
  • 退出:SSO 中心清除 Cookie + 各系统清除本地 Session

Token-Based SSO 详解

1. 跨域场景(前后端分离)

sequenceDiagram participant U as 浏览器 participant F as 前端A participant B as 后端A participant S as SSO中心 participant F2 as 前端B participant B2 as 后端B F->>S: POST /api/login {user,pwd} S->>F: 200 {token: jwt} F->>B: GET /api/user Header: Authorization: Bearer jwt B->>B: 验证 JWT → 成功 F2->>S: GET /api/user Header: Authorization: Bearer jwt S->>F2: 200 {userInfo}

2. JWT 结构

css 复制代码
Header.Base64.Payload.Base64.Signature
  • Header{"alg":"HS256","typ":"JWT"}
  • Payload{"sub":"alice","exp":1719999999}
  • SignatureHMACSHA256(header + "." + payload, secret)

OAuth2 + OpenID Connect 流程

1. 授权码模式(最常用)

sequenceDiagram participant U as 用户 participant C as 客户端 participant G as Google(IDP) participant R as Resource Server U->>C: 点击"用 Google 登录" C->>G: 302 → authorize?client_id=xxx&response_type=code&scope=openid U->>G: 登录并同意授权 G->>C: 302 → redirect_uri?code=AUTH_CODE C->>G: POST /token {code, client_secret} G->>C: 200 {access_token, id_token(JWT)} C->>R: GET /userinfo Header: Bearer access_token R->>C: 200 {sub: "alice", name: "Alice"}

JWT 实战(Spring Boot + Redis)

1. 项目结构

css 复制代码
src
 ├─ config/JwtConfig.java
 ├─ controller/LoginController.java
 ├─ filter/JwtFilter.java
 └─ SpringBootSsoApplication.java

2. 生成 JWT

java 复制代码
@Component
public class JwtProvider {

    private final String secret = "demoSecretKey";
    private final long validity = 3600_000; // 1h

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + validity))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}

3. 过滤器验证

java 复制代码
@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtProvider provider;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            if (provider.validateToken(token)) {
                String username = Jwts.parser()
                                      .setSigningKey(provider.secret)
                                      .parseClaimsJws(token)
                                      .getBody()
                                      .getSubject();
                SecurityContextHolder.getContext().setAuthentication(
                        new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList()));
            }
        }
        filterChain.doFilter(request, response);
    }
}

4. 登录接口

java 复制代码
@RestController
public class LoginController {

    @Autowired
    private JwtProvider provider;

    @PostMapping("/login")
    public Map<String, Object> login(@RequestBody LoginRequest req) {
        // 省略密码校验
        String token = provider.generateToken(req.getUsername());
        return Map.of("token", token);
    }
}

常见安全问题 & 防护

攻击 防护
Token 泄露 HTTPS + 短有效期
XSS 窃取 HttpOnly Cookie
CSRF SameSite=Strict + CSRF Token
重放攻击 JWT 过期时间 + Redis 黑名单

总结:一张图看懂 SSO

graph TD A[浏览器] -->|登录| B[SSO中心] B -->|颁发 Token| A A -->|携带 Token| C[系统A] A -->|携带 Token| D[系统B] C -->|验证 Token| B D -->|验证 Token| B

口诀一次登录,全网通行;一次退出,全网失效;Token 短,HTTPS 长。

相关推荐
叶小鸡4 分钟前
Java 篇-项目实战-苍穹外卖-笔记汇总
java·开发语言·笔记
AI人工智能+电脑小能手19 分钟前
【大白话说Java面试题】【Java基础篇】第22题:HashMap 和 HashSet 有哪些区别
java·开发语言·哈希算法·散列表·hash
juniperhan34 分钟前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
ID_1800790547335 分钟前
Python 实现亚马逊商品详情 API 数据准确性校验(极简可用 + JSON 参考)
java·python·json
c++之路1 小时前
C++23概述
java·c++·c++23
专注API从业者2 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
摇滚侠2 小时前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
古城小栈2 小时前
从 cargo-whero 库中,找到提升 rust 的契机
开发语言·后端·rust
keep one's resolveY2 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克33 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信