Kerberos认证实现疑问:仅返回Negotiate头能否获取Kerberos票据?
Kerberos认证问题排查与无Web依赖实现方案
为何拿到的是NTLM而非Kerberos票据?
仅返回WWW-Authenticate: Negotiate头的401响应没法强制Windows客户端选Kerberos——Negotiate是让客户端自行在Kerberos和NTLM二选一,出现NTLM大概率是以下环节出了问题:
- AD服务器端:
- 没给应用服务器创建正确的服务主体名称(SPN),或者SPN没绑定到应用服务器的服务账户(得用
setspn -S HTTP/你的应用服务器FQDN@域名.COM 域\服务账户命令配置) - 应用服务器的服务账户没有委派权限,或者SPN重复绑定
- 没给应用服务器创建正确的服务主体名称(SPN),或者SPN没绑定到应用服务器的服务账户(得用
- Windows客户端:
- 没把应用服务器的FQDN加入本地Intranet区域(IE/Edge在Internet选项里配,Chrome会继承这个设置)
- Kerberos缓存有无效票据,可跑
klist purge命令清空 - 客户端解析应用服务器FQDN到的IP不对,导致Kerberos请求失败,降级用NTLM
- 应用服务器端:只返回Negotiate头只是触发协商,但客户端发Kerberos令牌后,应用得用GSS-API/SPNEGO解析验证,不然客户端会 fallback到NTLM
必须用SPNEGO或GSS-API吗?
对,必须靠SPNEGO或GSS-API处理Kerberos令牌。Negotiate头只是让客户端发起认证协商,但Kerberos令牌的解析、验证(比如校验签名、取用户身份)都得依赖GSS-API(Java里是org.ietf.jgss包)或者基于它封装的SPNEGO实现,没这步根本拿不到有效的Kerberos身份信息。
无Spring Security Web依赖的Kerberos实现步骤
既然只能用AbstractPhaseInterceptor和InterceptionProvider,按下面的步骤来:
- 配Kerberos基础环境:
- 在应用服务器放正确的
krb5.conf(Linux)或krb5.ini(Windows),示例:[libdefaults] default_realm = YOURDOMAIN.COM dns_lookup_kdc = true dns_lookup_realm = true [realms] YOURDOMAIN.COM = { kdc = dc.yourdomain.com admin_server = dc.yourdomain.com } [domain_realm] .yourdomain.com = YOURDOMAIN.COM yourdomain.com = YOURDOMAIN.COM - 给JVM加参数指定配置文件路径:
-Djava.security.krb5.conf=/你的路径/krb5.conf
- 在应用服务器放正确的
- 写Kerberos认证拦截器:
- 继承
AbstractPhaseInterceptor,在拦截逻辑里处理请求头:- 检查请求有没有带
Authorization: Negotiate <token>头 - 没带的话,返回401并设置
WWW-Authenticate: Negotiate头 - 带了令牌就用GSS-API解析,核心代码示例:
GSSManager manager = GSSManager.getInstance(); GSSName serverName = manager.createName("HTTP/你的应用服务器FQDN@YOURDOMAIN.COM", GSSName.NT_HOSTBASED_SERVICE); GSSContext context = manager.createContext(serverName, null, null, GSSContext.DEFAULT_LIFETIME); byte[] token = Base64.getDecoder().decode(authToken); byte[] responseToken = context.acceptSecContext(token, 0, token.length); // 拿到认证用户的身份 String userName = context.getSrcName().toString(); context.dispose();
- 检查请求有没有带
- 把解析出的用户身份传给自定义认证逻辑,和现有的Basic、LDAP认证提供者整合
- 继承
- 绑定SPN并生成keytab:
- 在AD服务器用域管理员权限执行:
setspn -S HTTP/你的应用服务器FQDN YOURDOMAIN\应用服务账户,确保SPN唯一 - 生成keytab文件:
ktpass /princ HTTP/你的应用服务器FQDN@YOURDOMAIN.COM /mapuser YOURDOMAIN\应用服务账户 /pass * /out app-server.keytab /ptype KRB5_NT_PRINCIPAL /crypto AES256-SHA1 - 给JVM加参数指定keytab和登录配置:
-Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=/你的路径/jaas.conf - jaas.conf示例:
Krb5Login { com.sun.security.auth.module.Krb5LoginModule required useKeyTab=true keyTab="/你的路径/app-server.keytab" principal="HTTP/你的应用服务器FQDN@YOURDOMAIN.COM" storeKey=true useTicketCache=false; };
- 在AD服务器用域管理员权限执行:
之前配置无效的常见原因
- SPN和keytab配置不对,导致应用服务器没法验证Kerberos令牌
- krb5.conf/ini配置错误,比如KDC地址、域名称大小写不匹配(Kerberos对大小写敏感)
- 客户端没把应用服务器加入Intranet区域,浏览器默认不发Kerberos令牌
- 应用端没实现Kerberos令牌的解析逻辑,光返回Negotiate头完不成完整认证流程
内容的提问来源于stack exchange,提问作者Olga D
相关产品推荐
相关产品推荐

