单点登录到底是什么?


目录

  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)一次登录,全网通行一次退出,全网失效


核心概念

概念说明
CASCentral Authentication Service(中央认证服务)
Token登录后颁发的 凭证(JWT、Ticket)
TGTTicket Granting Ticket(CAS 术语)
STService Ticket(CAS 术语)
IdPIdentity Provider(身份提供者)
SPService Provider(服务提供者)

SSO 分类与对比

类型存储代表协议适用场景
Cookie-Based浏览器 CookieCAS 2.0/3.0同域名
Token-BasedHeader/LocalStorageJWT/OIDC跨域、前后端分离
OAuth2Access TokenOAuth2 + OIDC第三方登录

Cookie-Based SSO 详解

1. 架构图(同域名)

mermaid.png

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 结构

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. 项目结构

src
 ├─ config/JwtConfig.java
 ├─ controller/LoginController.java
 ├─ filter/JwtFilter.java
 └─ SpringBootSsoApplication.java

2. 生成 JWT

@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. 过滤器验证

@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. 登录接口

@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
CSRFSameSite=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
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]