本文目录导读:

- 核心原理(必知)
- 方案一:基于 Cookie + 共享 Session(最简单的同域名 SSO)
- 方案二:基于 JWT + 中央认证服务(最流行的跨域名 SSO)
- 方案三:基于 OAuth2 / CAS(企业级最强)
- 总结:如何选型?
- 关键安全提示
实现单点登录(SSO)在 Java 中是一个经典的企业级分布式系统问题,核心思想是一次登录,多系统通行。
由于“如何实现”这个问题范围较大,下面按照从简单到复杂、从原理到实践的顺序,给出几种主流的 Java 实现方案,并附上核心代码逻辑和架构图思路。
核心原理(必知)
所有 SSO 的实现都围绕着一个独立的认证中心(SSO Server)。
- 用户访问子系统 A,未登录 -> 重定向到 SSO Server。
- 用户输入账号密码,SSO Server 验证成功,创建全局会话(Global Session)并生成一个 Ticket。
- 浏览器携带 Ticket 跳回子系统 A,A 拿着 Ticket 去 SSO Server 验证。
- SSO Server 验证 Ticket 有效,通知 A 登录成功,A 创建自己的局部会话。
- 用户访问子系统 B,B 发现未登录 -> 重定向到 SSO Server。
- SSO Server 发现用户已有全局会话(通过 Cookie/Session),直接生成新 Ticket 并跳回 B,B 验证后登录。
基于 Cookie + 共享 Session(最简单的同域名 SSO)
适用场景: 多个子系统属于同一个顶级域名(如 a.example.com、b.example.com)。
实现思路:
- 将 Session 存储到 Redis 等中间件中(Session 共享)。
- 将 Cookie 的
Domain属性设置为.example.com。 - 所有子系统读取同一个 Redis Session 数据。
Java 核心步骤(以 Spring Boot + Redis 为例):
- 引入依赖:
spring-session-data-redis - 配置文件(application.yml):
spring: session: store-type: redis redis: host: 你的redis地址 server: servlet: session: cookie: domain: .example.com # 关键:顶级域名共享 - 所有子系统配置一样即可。
- 优点: 实现极简单,无需额外开发认证中心。
- 缺点: 无法跨顶级域名(如
a.com和b.com),Cookie 不安全(易被劫持,需配合 HTTPS)。
基于 JWT + 中央认证服务(最流行的跨域名 SSO)
适用场景: 跨不同域名、前后端分离、移动端。
核心组件:
- SSO Server(认证中心): 登录页面、Token 发放、JWT 生成与校验。
- Client 子系统(服务端): 拦截未登录请求,跳转 SSO Server。
- JWT 令牌: 携带用户身份信息,时效短,无状态。
Java 实现流程(伪代码 + 关键类):
SSO Server 登录接口
@RestController
@RequestMapping("/sso")
public class SSOAuthController {
@PostMapping("/login")
public Result login(String username, String password, HttpServletResponse response) {
// 1. 校验账号密码
User user = userService.authenticate(username, password);
if (user == null) {
return Result.error("登录失败");
}
// 2. 生成 JWT Token(使用 JJWT 或 io.jsonwebtoken 库)
String token = JwtUtils.createToken(user.getId(), user.getName(), 3600 * 1000); // 1小时过期
// 3. 将 token 写入 Cookie(HttpOnly, SameSite=Lax)或直接返回给前端
Cookie cookie = new Cookie("SSO_TOKEN", token);
cookie.setPath("/");
cookie.setDomain("sso.com"); // 仅认证中心的域名
cookie.setHttpOnly(true);
cookie.setSecure(true); // 强制 HTTPS
response.addCookie(cookie);
// 4. 重定向回子系统:携带临时票据 code
String code = UUID.randomUUID().toString();
// 将 code -> token 存入 Redis(短期有效,如 5 分钟)
redisTemplate.opsForValue().set("SSO_CODE:" + code, token, 5, TimeUnit.MINUTES);
return Result.success("登录成功", code); // 前端用这个 code 去回调子系统
}
}
Client 子系统的 Filter 拦截
public class SSOFilter extends OncePerRequestFilter {
@Autowired
private RestTemplate restTemplate; // 用于与 SSO Server 通信
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// 1. 检查本地会话(业务系统的 Session)
HttpSession session = request.getSession();
if (session.getAttribute("LOGIN_USER") != null) {
chain.doFilter(request, response);
return;
}
// 2. 检查是否有 callback 参数(SSO Server 跳转回来时携带的 ticket)
String ticket = request.getParameter("ticket");
if (StringUtils.hasText(ticket)) {
// 2.1 拿着 ticket 去 SSO Server 验证并获取用户信息
String verifyUrl = "http://sso.com/sso/verify?ticket=" + ticket;
ResponseEntity<UserInfo> resp = restTemplate.getForEntity(verifyUrl, UserInfo.class);
if (resp.getStatusCode().is2xxSuccessful() && resp.getBody() != null) {
// 2.2 验证成功,创建本地会话
session.setAttribute("LOGIN_USER", resp.getBody());
// 2.3 跳回本系统首页(去除 URL 中的 ticket 参数)
response.sendRedirect("/index");
return;
}
}
// 3. 未登录,也没有 ticket,重定向到 SSO Server
String redirectUrl = URLEncoder.encode("http://www.business.com/current/path", "UTF-8");
String ssoLoginUrl = "http://sso.com/login?redirect=" + redirectUrl;
response.sendRedirect(ssoLoginUrl);
}
}
SSO Server 验证 Ticket 接口
@GetMapping("/sso/verify")
public Result verifyTicket(@RequestParam("ticket") String ticket) {
String token = redisTemplate.opsForValue().get("SSO_CODE:" + ticket);
if (token == null) {
return Result.error("无效ticket");
}
// 解析 JWT,获取用户信息
UserInfo user = JwtUtils.parseToken(token);
if (user == null) {
return Result.error("token已过期");
}
// 删除已使用的 ticket(一次性)
redisTemplate.delete("SSO_CODE:" + ticket);
return Result.success(user);
}
- 优点: 无状态、跨域、适合分布式。
- 缺点: 未登录时重定向流程较复杂,需要维护 JWT 刷新机制。
基于 OAuth2 / CAS(企业级最强)
适用场景: 大型企业、需要对接第三方应用、要求标准协议。
推荐框架:
- CAS Server(Java 经典): Jedis 社区版,功能完善,官方提供了
cas-server-webapp,直接打包部署即可。 - Spring Security + OAuth2 Client: Spring Boot 官方支持,配置即可集成 SSO。
Spring Security + OAuth2 Client 配置示例(作为 Client 接入):
spring:
security:
oauth2:
client:
registration:
my-sso:
client-id: your-client-id
client-secret: your-client-secret
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: openid, profile
provider:
my-sso:
issuer-uri: https://your-sso-server.com/auth/realms/your-realm
- 优点: 标准化(OAuth2 / SAML2),支持社交登录、身份联合。
- 缺点: 配置复杂,需要维护认证中心(如 Keycloak、CAS Server)。
如何选型?
| 需求场景 | 推荐方案 | 复杂度 | 安全性 |
|---|---|---|---|
| 同域名下多个子系统 | Cookie + 共享Session | ⭐ | 中(需HTTPS) |
| 跨域名、前端分离、小型项目 | JWT + 中央认证 | ⭐⭐ | 高 |
| 企业级、多协议、对接第三方 | OAuth2 / CAS / Keycloak | ⭐⭐⭐⭐ | 极高 |
关键安全提示
- Cookie 必须设置
HttpOnly(防止 JS 盗取)。 - 生产环境强制 HTTPS(防止 Token 被中间人截获)。
- Ticket 必须一次性(防止 Replay Attack)。
- JWT 过期时间不宜过长(建议 15-30 分钟,配合 Refresh Token 使用)。
- SSO Server 与 Client 之间应使用内网或签名(防止伪造请求)。
如果你需要一个完整的可运行 Demo(Spring Boot + JWT + Redis),可以告诉我,我可以帮你写出更详细的代码结构(包括 JJWT 工具类、登录页面跳转逻辑等)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播