Spring Security Kerberos 2.0中如何从SpnegoAuthenticationToken获取PAC SIDs?
解决方案与版本支持说明
一、Spring Security Kerberos 2.0 可行实现方案
你可以通过自定义AuthenticationProvider绕过KerberosServiceAuthenticationProvider的限制,直接从SpnegoAuthenticationToken中提取PAC SIDs,具体实现如下:
1. 自定义认证提供者
实现AuthenticationProvider接口,专门处理SpnegoAuthenticationToken并解析PAC获取SIDs:
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.kerberos.authentication.SpnegoAuthenticationToken; import org.springframework.security.kerberos.authentication.KerberosTicket; import sun.security.krb5.internal.Ticket; import java.io.ByteArrayInputStream; import java.util.Base64; public class SpnegoPacAuthenticationProvider implements AuthenticationProvider { private final UserDetailsService userDetailsService; public SpnegoPacAuthenticationProvider(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Override public boolean supports(Class<?> authentication) { return SpnegoAuthenticationToken.class.isAssignableFrom(authentication); } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { SpnegoAuthenticationToken token = (SpnegoAuthenticationToken) authentication; KerberosTicket kerberosTicket = token.getTicket(); // 从Kerberos Ticket中提取并解析PAC SIDs String pacSids = extractPacSids(kerberosTicket); // 加载基础用户信息 UserDetails userDetails = userDetailsService.loadUserByUsername(token.getName()); // 将PAC SIDs注入自定义UserDetails对象(需提前实现带SIDs字段的UserDetails) if (userDetails instanceof CustomUserDetails customUser) { customUser.setPacSids(pacSids); } // 返回已完成认证的令牌 return new SpnegoAuthenticationToken(userDetails, userDetails.getAuthorities(), kerberosTicket); } private String extractPacSids(KerberosTicket kerberosTicket) { try { byte[] ticketBytes = Base64.getDecoder().decode(kerberosTicket.getEncoded()); Ticket ticket = new Ticket(new ByteArrayInputStream(ticketBytes)); // 此处需实现PAC数据的具体解析逻辑,可参考kerb4j的PAC解析代码 // 示例仅做占位,实际需替换为真实解析逻辑 return parseSidsFromPacData(ticket.getEncPart().getCipher()); } catch (Exception e) { throw new RuntimeException("Failed to extract PAC SIDs", e); } } private String parseSidsFromPacData(byte[] pacData) { // 实现PAC数据解析,提取用户SID和组SID return "S-1-5-21-XXX-XXX-XXX-1001,S-1-5-21-XXX-XXX-XXX-513"; } }
2. 配置Spring Security认证链
在Security配置中注册自定义Provider,替换默认的Kerberos认证提供者:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter; @Configuration @EnableWebSecurity public class SecurityConfig { private final UserDetailsService userDetailsService; public SecurityConfig(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .addFilterBefore(spnegoFilter(), SpnegoAuthenticationProcessingFilter.class); return http.build(); } @Bean public SpnegoAuthenticationProcessingFilter spnegoFilter() throws Exception { SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter(); filter.setAuthenticationManager(authenticationManager()); return filter; } @Bean public AuthenticationManager authenticationManager() { return new ProviderManager(new SpnegoPacAuthenticationProvider(userDetailsService)); } }
注意事项
- 解析PAC时,JDK内部的
sun.security.krb5包类存在版本兼容性风险,建议使用Apache Directory Server的kerberos-codec模块替代,保证解析逻辑稳定。 - 需自定义
UserDetails实现类,添加PAC SIDs的存储字段,方便后续权限判断使用。
二、2.1版本支持情况
目前Spring Security Kerberos官方的2.1版本规划中,暂未明确将支持AuthenticationUserDetailsService传入KerberosServiceAuthenticationProvider的功能列入开发计划。可关注项目的GitHub Issues或Milestone页面,获取功能迭代的最新动态。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

