一、单点登录(SSO)概述
单点登录(SSO, Single Sign-On)是一种认证机制,允许用户只需登录一次,即可访问多个相互信任的系统或应用,而不需要为每个系统重复登录。
二、SSO 演进路径
我们可以从以下几个阶段梳理其演进:
1. 基于 Session 的登录共享(早期)
-
原理:多个系统部署在同一域(或子域),共享 Cookie(Session ID);
-
代表:JSESSIONID、Token 写入 Cookie;
-
缺陷:
- 不支持跨域;
- Cookie 安全性差;
- 不适用于微服务架构。
2. CAS(Central Authentication Service)
-
原理:引入认证中心(CAS Server),各系统称为 CAS Client;
-
流程:
- 用户访问系统 -> 重定向至 CAS;
- CAS 认证成功发放 Ticket;
- Client 用 Ticket 交换用户信息;
-
优点:
- 标准协议,安全性强;
- 支持登出同步通知;
-
缺陷:
- 基于 Session,有状态;
- 跳转流程复杂;
- 服务注册/信任维护成本高;
- 不适配现代 SPA 前后端分离模式。
3. Token-Based SSO(JWT/Token)
-
核心思想:服务端不存储状态,客户端携带身份令牌;
-
典型实现:JWT(JSON Web Token);
-
优点:
- 无状态,适合微服务;
- 易扩展(加密、签名);
- 支持跨域;
-
缺点:
- Token 一旦泄露风险大;
- 不易主动失效(需配合 Redis 黑名单或刷新机制);
- 前端需妥善管理 Token。
4. OAuth2 / OpenID Connect 标准协议
-
代表平台:Google、微信、钉钉登录等;
-
优点:
- 认证与授权分离;
- 多终端接入支持;
- 标准化、高度安全;
-
缺陷:
- 实现复杂,尤其是权限粒度控制;
- 第三方平台授权变更时要维护兼容性。
5. 微服务网关 + Token + Redis 模式(现代实践)
现代企业系统通常采用如下组合:
组件 | 作用 |
---|---|
API Gateway(如 Spring Cloud Gateway) | 统一入口,进行 Token 验证、权限路由 |
Auth 服务(Spring Security) | 登录/登出、签发 Token、权限服务 |
JWT | 携带身份信息 |
Redis(如 Redisson) | Token 状态管理、黑名单、刷新机制 |
微服务(User、Order 等) | 解耦的业务模块,通过 Gateway 访问 |
三、SSO 最佳实践指南
✅ 身份令牌设计
-
使用 JWT,包含以下字段:
sub
: 用户IDexp
: 过期时间iat
: 签发时间roles
: 权限角色(用于 RBAC)
-
签名算法建议使用
HS256
或RS256
。
✅ Token 安全实践
- Token 加密传输(HTTPS);
- 前端使用
HttpOnly
Cookie 存储或本地存储(注意 XSS); - Token 一旦失效可使用 Redis 进行黑名单控制;
- 支持 Refresh Token 实现长登录。
✅ 微服务间统一认证
- 网关统一拦截校验 JWT;
- 用户信息通过
X-User
Header 传递到后端服务; - 后端服务不需重复认证,但可基于角色权限授权。
✅ 登出机制
- 登录状态在 Redis 存储(可配置多端登录);
- 登出时将 JWT 添加至 Redis 黑名单,或删除登录状态;
- Token 在过期后自动失效。
✅ 权限控制设计(RBAC)
- 用户角色与权限粒度控制应后端统一维护;
- JWT 中应包含角色或权限信息(或仅包含 userId,由后端动态查);
- 权限校验可在网关或各服务中进行。
四、典型架构图
[Browser / App]|v[API Gateway] --- JWT 验证 + 权限路由|+--- [Auth Service] --> Redis/Redisson -> 签发Token|+--- [User Service]|+--- [Order Service]
五、可选扩展能力
- ✅ 多端登录管理(每端维护独立 Token);
- ✅ 支持滑动过期(如 15分钟未操作就过期);
- ✅ 集成第三方认证平台(微信企业/钉钉 SSO);
- ✅ 接入 OAuth2 Server(如 Auth0、Spring Authorization Server);
- ✅ 会话追踪和审计(登录日志、设备管理)。
六、总结建议
方面 | 建议 |
---|---|
架构模式 | 网关 + Auth 服务 + Redis + JWT |
Token | JWT + RefreshToken + Redis |
鉴权粒度 | 网关做粗粒度鉴权,服务内做细粒度控制 |
异常处理 | JWT 无效、过期、被撤销统一返回 401 |
扩展性 | 预留 OAuth2 接入能力;Token 黑名单设计灵活 |
七、Spring Boot + Spring Security + Redisson + JWT + Spring Cloud Gateway 实现单点登录(SSO)的项目结构和代码示例,适用于微服务环境。
🧱 项目结构(简版)
sso-system/
├── gateway/ # 网关服务:认证拦截、转发
├── auth-service/ # 登录认证服务:签发 Token、登出
├── user-service/ # 示例业务服务:用户信息接口
├── common/ # 公共模块:JWT 工具、DTO、常量等
🔧 1. 公共模块 common
1.1 JWT 工具类
public class JwtUtil {private static final String SECRET = "sso-secret-key";public static String generateToken(String username) {return Jwts.builder().setSubject(username).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + 3600_000)) // 1小时.signWith(SignatureAlgorithm.HS256, SECRET).compact();}public static Claims parseToken(String token) {return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();}
}
🚪 2. 网关服务 gateway
2.1 pom.xml(关键依赖)
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
</dependencies>
2.2 JwtAuthFilter(全局过滤器)
@Component
public class JwtAuthFilter implements GlobalFilter, Ordered {@Autowiredprivate RedissonClient redisson;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String token = exchange.getRequest().getHeaders().getFirst("Authorization");if (token != null && token.startsWith("Bearer ")) {try {token = token.replace("Bearer ", "");Claims claims = JwtUtil.parseToken(token);// 黑名单判断if (redisson.getSet("jwt:blacklist").contains(token)) {return unauthorized(exchange);}// 添加用户信息头部String user = claims.getSubject();ServerHttpRequest newRequest = exchange.getRequest().mutate().header("X-User", user).build();return chain.filter(exchange.mutate().request(newRequest).build());} catch (Exception e) {return unauthorized(exchange);}}return unauthorized(exchange);}private Mono<Void> unauthorized(ServerWebExchange exchange) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}@Overridepublic int getOrder() {return -1;}
}
2.3 application.yml
spring:cloud:gateway:routes:- id: authuri: http://localhost:8081predicates:- Path=/auth/**- id: useruri: http://localhost:8082predicates:- Path=/user/**redis:host: 127.0.0.1port: 6379
🔐 3. 认证服务 auth-service
3.1 登录控制器
@RestController
@RequestMapping("/auth")
public class AuthController {@Autowiredprivate RedissonClient redisson;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody Map<String, String> req) {String username = req.get("username");String password = req.get("password");// 假设固定账户:admin/123456if ("admin".equals(username) && "123456".equals(password)) {String token = JwtUtil.generateToken(username);redisson.getMap("jwt:token").put(username, token);return ResponseEntity.ok(Map.of("token", token));}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");}@PostMapping("/logout")public ResponseEntity<?> logout(@RequestHeader("Authorization") String token) {if (token != null && token.startsWith("Bearer ")) {token = token.replace("Bearer ", "");redisson.getSet("jwt:blacklist").add(token);}return ResponseEntity.ok("Logged out");}
}
👤 4. 用户服务 user-service
4.1 控制器
@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/info")public ResponseEntity<?> userInfo(@RequestHeader("X-User") String username) {return ResponseEntity.ok(Map.of("user", username, "role", "admin"));}
}
⚙️ 5. Redisson 配置(通用)
redisson-config.yml
singleServerConfig:address: "redis://127.0.0.1:6379"
Java 配置
@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() throws IOException {Config config = Config.fromYAML(new ClassPathResource("redisson-config.yml").getInputStream());return Redisson.create(config);}
}
✅ 接口调用流程
- 调用
POST /auth/login
获取 Token; - 请求头加上
Authorization: Bearer <token>
; - 访问
GET /user/info
会被 Gateway 验证; - 可调用
POST /auth/logout
退出登录,Token 进入黑名单。
📌 补充说明
-
安全性增强:
- 加密 Token、添加刷新机制;
- Redis 维护每个用户的登录态;
-
权限系统扩展:
- JWT 添加
roles
; - 在 Gateway 或微服务中做 RBAC 检查;
- JWT 添加
-
多端登录限制:
- Redis 存储不同端(PC/Mobile)对应 Token;
- 登出清除端上 Token。