1. 概述
在本教程中,我们将了解 Kerberos 认证协议的基础知识,并探讨为何在 Kerberos 中需要 SPNEGO。
接着,我们将使用 Spring Security 的 Kerberos 扩展模块,快速搭建一个支持 Kerberos + SPNEGO 的 Web 应用。
⚠️ 本文假设读者对认证机制、Spring Security 和 Web 应用有基本了解。我们将跳过一些基础内容,聚焦在 Kerberos 与 SPNEGO 的整合与实践上。
2. 理解 Kerberos
Kerberos 是一种网络认证协议,由 MIT 在上世纪 80 年代开发。虽然它诞生已久,但至今仍被广泛使用,尤其在 Windows 环境中是默认的认证机制。
Kerberos 是基于票据(Ticket)的认证协议,允许网络中的节点互相验证身份。
2.1. Kerberos 的一个简单使用场景
假设一个用户想通过邮件客户端从邮件服务器拉取邮件。为了安全通信,客户端和服务器之间需要互相认证。
Kerberos 引入了一个第三方角色:KDC(Key Distribution Centre),它与网络中的每个节点都建立信任关系。通过 KDC,节点之间无需直接交换密码即可完成认证。
2.2. Kerberos 协议的核心特性
- 所有节点(客户端和服务器)信任 KDC
- 密码从不在网络上传输
- 客户端与服务器之间的信任是基于它们都能用与 KDC 共享的密钥解密消息
- 客户端与服务器之间是双向认证
- 客户端可缓存票据,实现单点登录(SSO)
- 使用时间戳生成的一次性 Authenticator 消息
- 所有三方时间需大致同步
虽然这些只是 Kerberos 的冰山一角,但已经足够帮助我们继续本教程。
3. 理解 SPNEGO
SPNEGO 全称 Simple and Protected GSS-API Negotiation Mechanism,是 GSS-API 的一部分。
GSS-API(Generic Security Service Application Program Interface)是 IETF 标准,用于客户端与服务端之间以安全、厂商无关的方式通信。
✅ SPNEGO 的作用是:让客户端和服务端协商使用哪种安全机制,比如 Kerberos 或 NTLM。
4. 为什么在 Kerberos 中需要 SPNEGO?
Kerberos 是一个运行在传输层的协议(如 TCP/UDP),适合传统网络服务(如 Telnet、FTP 等)。但 Web 应用运行在更高层(HTTP),无法直接使用 Kerberos。
这时 SPNEGO 就派上用场了。它使得浏览器和 Web 服务器之间可以通过 HTTP 协议交换 Kerberos 票据,将 Kerberos 票据封装为 SPNEGO Token,通过 HTTP Header 传输。
✅ 流程简要说明:
- 客户端向 KDC 认证并缓存 TGT(Ticket Granting Ticket)
- 浏览器配置为支持 SPNEGO 和 Kerberos
- Web 应用也配置为支持 SPNEGO 和 Kerberos
- 浏览器访问受保护资源时,服务器返回 “Negotiate” 挑战
- 浏览器将 Service Ticket 封装为 SPNEGO Token,通过 HTTP Header 发送
5. 前置准备
要搭建支持 Kerberos 的 Web 应用,我们需要完成以下准备工作:
5.1. 配置 KDC
Kerberos 的生产环境配置不在本教程范围内,但我们可以使用以下开源实现:
- MIT Kerberos v5
- Apache Kerby(支持 Java)
- Windows Server Active Directory
- Heimdal Kerberos
建议使用 Docker 运行 Apache Kerby 快速搭建测试环境。
5.2. 在 KDC 中创建用户(Principal)
我们需创建两个 Principal:
$ kadmin: addprinc -randkey kchandrakant -pw password
Principal "[email protected]" created.
注册 Web 应用:
$ kadmin: addprinc -randkey HTTP/[email protected] -pw password
Principal "HTTP/[email protected]" created.
⚠️ 注意命名规范:HTTP/<域名>@REALM
,这与浏览器自动生成的 SPN 匹配。
导出 keytab 文件供 Web 应用使用:
$ kadmin: ktadd -k baeldung.keytab HTTP/[email protected]
5.3. 浏览器配置
现代浏览器(如 Chrome)默认支持 SPNEGO。我们可以配置浏览器启用集成认证,但为了演示清晰,建议手动输入用户名密码。
5.4. 域名配置
Kerberos 不支持 localhost
或 IP 地址。解决方法是配置 hosts 文件:
127.0.0.1 demo.kerberos.bealdung.com
6. 使用 Spring Security 实现 SPNEGO + Kerberos
Spring Security 提供了 Kerberos 扩展模块,可非常方便地集成 Kerberos + SPNEGO。
6.1. Maven 依赖
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-web</artifactId>
<version>${kerberos.extension.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-client</artifactId>
<version>${kerberos.extension.version}</version>
</dependency>
6.2. SPNEGO Filter 配置
在 Spring Security 中添加 SPNEGO Filter:
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManager),
BasicAuthenticationFilter.class);
}
定义 Filter Bean:
@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
6.3. Kerberos 认证配置
配置 AuthenticationManager:
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider())
.build();
}
配置 KerberosAuthenticationProvider:
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
provider.setKerberosClient(client);
provider.setUserDetailsService(userDetailsService());
return provider;
}
配置 KerberosServiceAuthenticationProvider:
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(userDetailsService());
return provider;
}
配置 Kerberos Ticket Validator:
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal("HTTP/[email protected]");
ticketValidator.setKeyTabLocation(new FileSystemResource("baeldung.keytab"));
return ticketValidator;
}
6.4. UserDetailsService 的作用
Kerberos 只负责身份认证,不提供用户授权信息。因此我们需要实现 UserDetailsService 来补充用户权限信息。
你可以简单实现一个:
@Bean
public UserDetailsService userDetailsService() {
return username -> new User(username, "", AuthorityUtils.createAuthorityList("ROLE_USER"));
}
6.5. 启动应用
启动应用后,访问任意页面应触发浏览器弹出认证框,输入 Kerberos 用户名密码后,浏览器会自动构建 SPNEGO Token 并发送给服务端完成认证。
⚠️ 踩坑提醒:Kerberos 对配置非常敏感,比如域名、Principal 名、时间同步、keytab 文件路径等,稍有错误就会认证失败,建议逐一排查。
7. 实际应用场景
虽然 Kerberos 看起来有些“古老”,但它在企业内部系统中依然非常实用,尤其是:
- 企业内部已有 Kerberos/AD 基础设施
- 多个 Web 应用希望接入统一认证体系
- 实现浏览器自动登录(Integrated Authentication)
✅ 使用 SPNEGO + Kerberos 可以实现浏览器与 Web 应用的无缝认证,无需用户重复输入账号密码,用户体验优秀。
8. 小结
本文我们了解了 Kerberos 的基本原理,以及 SPNEGO 在 Web 应用中如何帮助我们实现 Kerberos 认证。
我们还通过 Spring Security 快速搭建了一个支持 SPNEGO + Kerberos 的 Web 应用,涵盖了配置 Kerberos Principal、keytab 文件、Spring Security 配置等关键步骤。
虽然 Kerberos 的配置过程较为繁琐,但它在企业级认证中依然具有不可替代的地位。
✅ 完整代码示例可参考:GitHub
如需进一步学习 Kerberos 和 SPNEGO,建议阅读以下资料: