单点登录到底是什么?

单点登录到底是什么?

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 长。

相关推荐
爬山算法2 分钟前
Hibernate(74)如何在CQRS架构中使用Hibernate?
java·架构·hibernate
jjjava2.011 分钟前
深入解析Set与Map的奥秘
java·开发语言
白宇横流学长16 分钟前
基于Java的火车票订票系统的设计与开发
java·开发语言
黎雁·泠崖16 分钟前
Java核心基础API学习总结:从Object到包装类的核心知识体系
java·开发语言·学习
Yvonne爱编码19 分钟前
JAVA数据结构 DAY1-集合和时空复杂度
java·数据结构·python
win x35 分钟前
JavaSE(基础)高频面试点及 知识点
java·面试·职场和发展
Terio_my36 分钟前
简要 Java 面试题学习
java·开发语言·学习
好好研究1 小时前
Spring Boot - Thymeleaf模板引擎
java·spring boot·后端·thymeleaf
爬山算法1 小时前
Hibernate(76)如何在混合持久化环境中使用Hibernate?
java·后端·hibernate
编程彩机1 小时前
互联网大厂Java面试:从分布式缓存到消息队列的技术场景解析
java·redis·面试·kafka·消息队列·微服务架构·分布式缓存