設計一個 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 等框架進行優化。