单点登录架构:SAML/OIDC/CAS
单点登录架构:SAML/OIDC/CAS
SAML协议实现
SAML(Security Assertion Markup Language)是企业级SSO的标准协议,基于XML交换身份断言。
// SAML服务提供商实现
@Component
public class SAMLServiceProvider {
private final SAMLConfig config;
private final IdentityProviderRepository idpRepository;
public AuthnRequest createAuthnRequest(String idpId) {
IDPConfig idp = idpRepository.findById(idpId);
AuthnRequest request = new AuthnRequest();
request.setID(generateRequestId());
request.setIssueInstant(new DateTime());
request.setVersion("2.0");
request.setDestination(idp.getSSOUrl());
request.setAssertionConsumerServiceURL(config.getAcsUrl());
request.setProtocolBinding(SAMLConstants.SAML2_HTTP_POST_BINDING);
// 签名请求
return signRequest(request);
}
public Assertion processResponse(String samlResponse) {
// 解码响应
String decoded = Base64.getDecoder().decodeToString(samlResponse);
// 验证签名
Response response = unmarshallAndVerify(decoded);
// 验证断言
Assertion assertion = response.getAssertions().get(0);
validateAssertion(assertion);
return assertion;
}
}
OpenID Connect实现
OIDC在OAuth2基础上增加了身份层,提供标准化的身份认证功能。
// OIDC身份提供商
@Component
public class OIDCIdentityProvider {
private final OIDCConfig config;
private final KeyManager keyManager;
@GetMapping("/.well-known/openid-configuration")
public ResponseEntity<Map<String, Object>> getDiscovery() {
Map<String, Object> discovery = new HashMap<>();
discovery.put("issuer", config.getIssuer());
discovery.put("authorization_endpoint", config.getIssuer() + "/authorize");
discovery.put("token_endpoint", config.getIssuer() + "/token");
discovery.put("userinfo_endpoint", config.getIssuer() + "/userinfo");
discovery.put("jwks_uri", config.getIssuer() + "/.well-known/jwks.json");
discovery.put("supported_signing_algorithms", Arrays.asList("RS256", "RS512"));
discovery.put("supported_grant_types", Arrays.asList("authorization_code", "refresh_token"));
return ResponseEntity.ok(discovery);
}
@GetMapping("/.well-known/jwks.json")
public ResponseEntity<Map<String, Object>> getJWKS() {
JWKS jwks = keyManager.getPublicKeys();
return ResponseEntity.ok(jwks.toMap());
}
@PostMapping("/token")
public ResponseEntity<TokenResponse> token(@RequestBody TokenRequest request) {
// 验证授权码
AuthorizationCode code = validateAuthorizationCode(request.getCode());
// 生成ID Token
String idToken = generateIDToken(code.getUser(), code.getNonce());
// 生成访问令牌
String accessToken = generateAccessToken(code.getUser(), code.getScope());
return ResponseEntity.ok(TokenResponse.builder()
.idToken(idToken)
.accessToken(accessToken)
.tokenType("Bearer")
.expiresIn(3600)
.build());
}
private String generateIDToken(User user, String nonce) {
return Jwts.builder()
.setSubject(user.getId())
.setIssuer(config.getIssuer())
.setAudience(user.getClientId())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.claim("nonce", nonce)
.claim("auth_time", user.getLastAuthTime())
.signWith(keyManager.getSigningKey(), SignatureAlgorithm.RS256)
.compact();
}
}
CAS协议实现
CAS(Central Authentication Service)是一种简单高效的SSO协议。
// CAS服务票据验证
@Component
public class CASTicketValidator {
private final TicketRegistry ticketRegistry;
private final ServiceRegistry serviceRegistry;
public CASResponse validateTicket(String ticketId, String serviceUrl) {
// 获取服务票据
ServiceTicket ticket = ticketRegistry.getServiceTicket(ticketId);
if (ticket == null || ticket.isExpired()) {
return CASResponse.failure("INVALID_TICKET", "票据无效或已过期");
}
// 验证服务URL
if (!ticket.getServiceUrl().equals(serviceUrl)) {
return CASResponse.failure("INVALID_SERVICE", "服务URL不匹配");
}
// 获取TGT(Ticket Granting Ticket)
TicketGrantingTicket tgt = ticket.getTgt();
// 创建PT(Proxy Ticket)用于代理
ProxyTicket pt = createProxyTicket(tgt, serviceUrl);
// 返回成功响应
return CASResponse.success(
tgt.getUser().getAttributes(),
pt != null ? pt.getId() : null
);
}
}
SSO架构对比与选择
# SSO协议对比
sso_protocols:
saml:
description: "企业级SSO标准"
use_cases:
- 企业内部系统集成
- 云服务身份联邦
- 高安全性要求场景
pros:
- 成熟稳定
- 功能完善
- 支持复杂的属性传输
cons:
- 实现复杂
- XML开销大
- 移动端支持有限
oidc:
description: "现代身份认证标准"
use_cases:
- Web和移动应用
- API认证
- 社交登录集成
pros:
- 基于OAuth2
- JSON格式轻量
- 移动端友好
cons:
- 相对较新
- 功能不如SAML完善
cas:
description: "简单SSO协议"
use_cases:
- 高校和教育机构
- 中小企业
- 快速部署需求
pros:
- 实现简单
- 部署快速
- 轻量级
cons:
- 功能相对有限
- 扩展性一般
SSO架构选择需要根据组织规模、安全需求和技术栈综合考虑,不同协议各有适用场景。