单点登录(SSO)系统
设计一个 Java 单点登录(SSO)系统需要解决跨系统认证和会话共享问题。以下是核心设计和实现方案,包含关键组件和代码示例:
一、核心概念
- 认证中心 (Auth Center):中央身份验证服务
- 令牌 (Token):用户身份凭证(推荐 JWT)
- 应用系统:接入 SSO 的子系统
二、SSO 登录流程
三、关键组件实现
1. JWT 工具类
import io.jsonwebtoken.*;
import java.util.Date;public class JwtUtil {private static final String SECRET = "your-256-bit-secret";private static final long EXPIRATION = 3600000; // 1小时public static String generateToken(String username) {return Jwts.builder().setSubject(username).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)).signWith(SignatureAlgorithm.HS256, SECRET).compact();}public static String parseToken(String token) {try {return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getSubject();} catch (Exception e) {throw new RuntimeException("Token invalid");}}
}
2. 认证中心登录接口
@RestController
@RequestMapping("/auth")
public class AuthController {@PostMapping("/login")public ResponseEntity<?> login(@RequestBody LoginRequest request, HttpServletResponse response) {// 1. 验证用户名密码(省略数据库验证)if (!"admin".equals(request.getUsername()) || !"123456".equals(request.getPassword())) {return ResponseEntity.status(401).body("Invalid credentials");}// 2. 生成JWTString token = JwtUtil.generateToken(request.getUsername());// 3. 设置全局会话(Redis存储)redisTemplate.opsForValue().set("SESSION:" + token, request.getUsername());// 4. 返回Token给客户端return ResponseEntity.ok(new TokenResponse(token));}
}
3. Token 验证接口
@RestController
@RequestMapping("/auth")
public class AuthController {@GetMapping("/validate")public ResponseEntity<?> validateToken(@RequestParam String token) {try {// 1. 验证JWT有效性String username = JwtUtil.parseToken(token);// 2. 检查Redis会话是否存在if (redisTemplate.hasKey("SESSION:" + token)) {return ResponseEntity.ok(username);}return ResponseEntity.status(401).body("Session expired");} catch (Exception e) {return ResponseEntity.status(401).body("Invalid token");}}
}
4. 应用系统拦截器
public class SSOInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession();// 1. 检查本地会话if (session.getAttribute("user") != null) {return true;}// 2. 检查请求中是否有TokenString token = request.getParameter("sso_token");if (token != null) {// 调用认证中心验证TokenString username = authService.validateToken(token);if (username != null) {session.setAttribute("user", username);return true;}}// 3. 重定向到认证中心登录String redirectUrl = "http://auth-center/login?redirect=" + URLEncoder.encode(request.getRequestURL().toString(), "UTF-8");response.sendRedirect(redirectUrl);return false;}
}
四、关键配置项
组件 | 配置示例 | 说明 |
---|---|---|
Redis | spring.redis.host=localhost | 存储全局会话 |
JWT | jwt.secret=your-256-bit-secret | HS256算法密钥 |
回调地址 | app.callback-url=http://app1/callback | 子系统注册的回调地址 |
五、安全增强措施
- HTTPS:所有通信强制使用 HTTPS
- 双重验证:JWT + Redis会话检查防止伪造
- CSRF防护:登录时生成随机 state 参数
- 黑名单:注销时加入 JWT 黑名单
- 令牌刷新:使用 refresh token 机制
六、扩展设计
- OAuth2集成:支持第三方登录(微信/Google)
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(ClientRegistration.withRegistrationId("weixin").clientId("weixin-appid").clientSecret("weixin-secret").authorizationUri("https://...").build());
}
- 跨域支持:配置
@CrossOrigin
解决前后端分离问题 - 会话同步:使用 Redis Pub/Sub 实现全局注销
七、部署架构
+-----------------+| 用户浏览器 |+--------+--------+| (重定向)v
+----------------+ +-----+------+ +----------------+
| 应用系统A |<--Token-->| 认证中心 |<--DB-->| 用户数据库 |
| (http://app1) | | (独立服务) | | |
+----------------+ +-----^------+ +----------------+| (重定向)
+----------------+ |
| 应用系统B |<---------------+
| (http://app2) |
+----------------+
通过以上设计,可实现:
- 一处登录,多系统通用
- 会话状态集中管理
- 安全可靠的 Token 机制
- 支持高并发和分布式部署
完整实现需补充数据库交互、错误处理、日志监控等模块,并参考 Spring Security OAuth2 或 Apache Shiro 等框架进行优化。