本文目录导读:

- 目录导读
- 单点登录(SSO)核心原理与适用场景
- Java SSO 的技术选型与架构设计
- 基于Redis + Token 的轻量级SSO案例实现
- 基于CAS协议的企业级SSO案例实现
- 常见问题与解决方案(Q&A)
- 安全性加固与性能优化建议
- SSO落地的关键考量
如何用Java案例实现单点登录?从原理到实战
目录导读
- 单点登录(SSO)核心原理与适用场景
- Java SSO 的技术选型与架构设计
- 基于Redis + Token 的轻量级SSO案例实现
- 基于CAS协议的企业级SSO案例实现
- 常见问题与解决方案(Q&A)
- 安全性加固与性能优化建议
- SSO落地的关键考量
单点登录(SSO)核心原理与适用场景
1 什么是单点登录?
单点登录(Single Sign-On,简称SSO)是一种身份验证机制,允许用户使用一组凭据(如用户名/密码)登录一次,即可访问多个相互信任的应用程序系统,登录了公司OA系统,无需再次登录即可访问ERP、CRM等子系统。
2 工作原理
SSO的核心是建立一个独立的认证中心(Authentication Center),所有子系统将认证请求统一交给该中心处理,标准流程如下:
- 用户访问子系统A,未登录 → 重定向到认证中心。
- 用户输入凭据,认证中心验证成功 → 生成全局票据(如Token)。
- 认证中心将票据下发,并重定向回子系统A。
- 子系统A携带票据向认证中心验证,验证通过后建立本地会话。
- 用户访问子系统B时,B重定向到认证中心,认证中心发现用户已登录(凭票据),直接下发新票据给B。
3 适用场景
- 企业级应用:多系统(CRM、HR、财务)统一身份管理。
- 微服务架构:不同服务模块共享用户登录状态。
- B2B/C SaaS平台:集成第三方系统(如GitHub、Google登录)。
Java SSO 的技术选型与架构设计
1 常见SSO实现方案对比
| 方案 | 复杂度 | 安全等级 | 适用场景 |
|---|---|---|---|
| 基于Cookie + 共享Session | 低 | 低 | 小规模内网(不推荐) |
| 基于Redis + Token | 中 | 中 | 中小型系统,可控性高 |
| CAS(Central Authentication Service) | 高 | 高 | 大型企业/高校 |
| OAuth2 / OIDC | 中高 | 高 | 开放授权 + 单点登录 |
| SAML 2.0 | 高 | 高 | 与老系统/退换货系统集成 |
2 Java技术栈推荐
- Spring Boot:快速构建微服务。
- Spring Security:用于认证与授权框架。
- Redis:存储Token和会话数据(高性能 + 共享)。
- JWT(JSON Web Token):生成无状态Token。
- CAS Server:基于Apereo CAS的开源方案。
3 架构图解(文字描述)
[用户浏览器] ⇄ [系统A] ⇄ [认证中心(CAS/JWT验证)]
⇅
[Redis共享]
⇅
[系统B]
基于Redis + Token 的轻量级SSO案例实现
本案例适合中小团队快速搭建SSO,无需额外安装CAS服务器。
1 项目结构
sso-server:认证中心,负责登录验证、Token生成与校验。client-app1、client-app2:集成SSO的子系统。
2 核心代码(关键步骤)
步骤1:认证中心登录接口(sso-server)
@RestController
public class AuthController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@PostMapping("/sso/login")
public String login(@RequestParam String username, @RequestParam String password) {
// 1. 验证用户名密码(略)
// 2. 生成全局Token(JWT)
String globalToken = UUID.randomUUID().toString();
String jwt = Jwts.builder()
.setSubject(username)
.claim("token", globalToken)
.setExpiration(new Date(System.currentTimeMillis() + 3600_000))
.signWith(SignatureAlgorithm.HS256, "secret-key")
.compact();
// 3. 存入Redis(key=globalToken, value=username)
redisTemplate.opsForValue().set(globalToken, username, 1, TimeUnit.HOURS);
return jwt;
}
@GetMapping("/sso/verify")
public String verify(@RequestParam String jwt) {
try {
Claims claims = Jwts.parser()
.setSigningKey("secret-key")
.parseClaimsJws(jwt)
.getBody();
String token = (String) claims.get("token");
// 检查Redis是否存在该token
if (redisTemplate.hasKey(token)) {
return "valid"; // 有效
}
} catch (Exception e) {
return "invalid";
}
return "invalid";
}
}
步骤2:子系统客户端拦截器(client-app1)
@Component
public class SSOInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("user") != null) {
return true; // 已有本地会话
}
// 获取全局Token(从Cookie或URL参数)
String globalToken = CookieUtil.getCookie(request, "global-token");
if (globalToken == null) {
response.sendRedirect("http://sso-server.com/sso/login?redirect=" + request.getRequestURL());
return false;
}
// 调用认证中心验证
ResponseEntity<String> resp = restTemplate.getForEntity(
"http://sso-server.com/sso/verify?jwt=" + globalToken,
String.class);
if ("valid".equals(resp.getBody())) {
// 创建本地会话
session.setAttribute("user", getUserFromToken(globalToken));
return true;
}
response.sendRedirect("http://sso-server.com/sso/login");
return false;
}
}
3 实现要点
- 无状态Token:JWT自带过期时间,减少数据库查询。
- Redis共享:所有子系统都能验证Token的有效性。
- 回调机制:登录成功的重定向URL,自动携带票据。
基于CAS协议的企业级SSO案例实现
CAS(Central Authentication Service)是最成熟的SSO开源方案之一,适用于大规模企业。
1 环境准备
- 下载Apereo CAS WAR包(或使用Docker)。
- 配置SSL证书(生产环境必须HTTPS)。
- 将CAS部署到Tomcat或Spring Boot中。
2 集成Spring Boot Client
pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
application.yml 配置
cas:
server-url-prefix: https://cas.example.com/cas
server-login-url: https://cas.example.com/cas/login
client-host-url: http://client1.example.com
authentication:
user-details:
enabled: true
3 安全配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ServiceProperties serviceProperties;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login/**", "/logout/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint())
.and()
.addFilter(casAuthenticationFilter())
.logout().logoutSuccessUrl("/logout");
}
private CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
entryPoint.setLoginUrl("https://cas.example.com/cas/login");
entryPoint.setServiceProperties(serviceProperties);
return entryPoint;
}
}
4 优势与局限
- 优势:成熟稳定,支持Ticket验证、代理认证。
- 局限:配置繁琐,需要独立部署CAS服务器。
常见问题与解决方案(Q&A)
Q1:登录成功但跳转回子系统后仍显示未登录?
原因:通常是票据(Ticket)未正确传递或验证失败。
解决方案:
- 检查子系统与CAS服务器的SSL证书是否一致。
- 确保Service URL与子系统实际地址匹配(包括端口号)。
- 查看CAS日志,确认Ticket是否被消费且未过期。
Q2:如何实现单点登出(SLO)?
实现方式:
- 基于Redis方案:子系统登出时,删除Redis中对应的全局Token,并通知其他子系统清除本地会话。
- CAS方案:CAS支持SLO协议,登出时向所有注册的子系统发送登出请求(建议使用MQ异步通知)。
Q3:跨域问题如何处理?
解决方案:
- 使用CORS(跨域资源共享)配置允许认证中心的域名。
- 使用Nginx反向代理统一域名(如
*.mycompany.com),避免跨域。 - Token放在Cookie中时,设置
SameSite=None; Secure以支持跨站传送。
Q4:Token泄露如何做安全防护?
建议措施:
- 使用HTTPS加密所有通信。
- Token添加设备指纹(如User-Agent + IP地址)。
- 设置较短的有效期,定期刷新(如30分钟)。
- 敏感操作(修改密码)需二次认证(MFA)。
安全性加固与性能优化建议
1 安全加固
- 日志审计:记录SSO登录/登出、Token校验失败事件。
- 黑名单机制:Redis中维护被攻击的Token黑名单。
- IP白名单:限制特定接口只允许子系统IP访问。
- 防止重放攻击:在Token中加入Nonce(一次性随机数)。
2 性能优化
- Redis集群:避免单点故障和性能瓶颈。
- 数据库索引:若Token存储使用数据库,建立哈希索引。
- 缓存User信息:子系统内缓存用户角色和权限,减少回调。
- 异步处理:日志写入、登出通知使用异步线程。
SSO落地的关键考量
实现Java单点登录并非简单引入一个依赖,而是需要从业务需求、安全等级、团队能力三个维度做选择:
- 小团队快速验证 → 选
Redis + Token方案(代码可控、成本低)。 - 中型多系统集成 → 推荐
OAuth2 + JWT,兼顾扩展性与授权能力。 - 大型企业合规需求 → 拥抱
CAS或SAML 2.0,同时准备好运维团队。
无论哪种方案,永远不要迷信“一次密码永不过期”,定期强制修改密码、启用MFA、监控异常访问日志,才是SSO长期稳定运行的关键。
文末提示:
如果你现在准备在Java项目中落地SSO,建议先搭建一个最小原型(只用两个Spring Boot应用加Redis),验证跨应用状态共享是否顺畅,再逐步加入复杂权限控制。