Spring 和 Spring Security Kerberos
本参考文档部分解释了 Spring Security Kerberos 为任何基于 Spring 的应用程序提供的核心功能。
身份验证提供者 描述了身份验证提供者支持。
Spnego 协商 描述了 Spnego 协商支持。
使用 KerberosRestTemplate 描述了 RestTemplate 支持。
身份验证提供者
使用 JavaConfig 进行提供者配置。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
KerberosAuthenticationProvider kerberosAuthenticationProvider = kerberosAuthenticationProvider();
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();
ProviderManager providerManager = new ProviderManager(kerberosAuthenticationProvider,
kerberosServiceAuthenticationProvider);
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout()
.permitAll()
.and()
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider())
.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),
BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
provider.setKerberosClient(client);
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
}
Spnego 协商
使用 JavaConfig 进行 Spnego 配置。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Value("${app.ad-domain}")
private String adDomain;
@Value("${app.ad-server}")
private String adServer;
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.ldap-search-base}")
private String ldapSearchBase;
@Value("${app.ldap-search-filter}")
private String ldapSearchFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = activeDirectoryLdapAuthenticationProvider();
ProviderManager providerManager = new ProviderManager(kerberosServiceAuthenticationProvider,
activeDirectoryLdapAuthenticationProvider);
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout()
.permitAll()
.and()
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider())
.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),
BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
return new ActiveDirectoryLdapAuthenticationProvider(adDomain, adServer);
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(ldapUserDetailsService());
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() throws Exception {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
contextSource.setLoginConfig(loginConfig());
return contextSource;
}
public SunJaasKrb5LoginConfig loginConfig() throws Exception {
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
loginConfig.afterPropertiesSet();
return loginConfig;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() throws Exception {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service =
new LdapUserDetailsService(userSearch, new ActiveDirectoryLdapAuthoritiesPopulator());
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
}
使用 KerberosRestTemplate
如果需要以编程方式访问 Kerberos 保护的 Web 资源,我们有 `KerberosRestTemplate`,它扩展了 `RestTemplate` 并在委托给实际的 RestTemplate 方法之前执行必要的登录操作。您基本上有几个选项来配置此模板。
-
如果要使用缓存的票据,请将 `keyTabLocation` 和 `userPrincipal` 留空。
-
如果要使用 keytab 文件,请使用 `keyTabLocation` 和 `userPrincipal`。
-
如果要自定义 `Krb5LoginModule` 选项,请使用 `loginOptions`。
-
使用自定义的 httpClient。
使用票据缓存。
public void doWithTicketCache() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate();
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
使用 keytab 文件。
public void doWithKeytabFile() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate("/tmp/user2.keytab", "[email protected]");
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
使用 LDAP 服务进行身份验证
在大多数示例中,我们使用 `DummyUserDetailsService`,因为 Kerberos 身份验证成功后不一定需要查询真实的用户详细信息,并且我们可以使用 Kerberos 主体信息来创建该虚拟用户。但是,有一种方法可以以安全的方式访问 Kerberized LDAP 服务并从中查询用户详细信息。
`KerberosLdapContextSource` 可用于通过 Kerberos 绑定到 LDAP,这至少被证明与 Windows AD 服务配合良好。
@Value("${app.ad-server}")
private String adServer;
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.ldap-search-base}")
private String ldapSearchBase;
@Value("${app.ldap-search-filter}")
private String ldapSearchFilter;
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
contextSource.setLoginConfig(loginConfig);
return contextSource;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service = new LdapUserDetailsService(userSearch);
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
|
示例 安全服务器 Windows 身份验证示例 目前已配置为在通过 Kerberos 发生身份验证时从 AD 查询用户详细信息。 |