我想在单个spring安全配置中配置所有三种(Kerberos,X509和OAuth2)身份验证机制。
情况1 :所有功能都应同时正常运行。
情况2 :如果第一个身份验证提供程序返回任何异常或未经授权,则其他身份验证提供程序会自动触发。
示例 :如果请求被kerberos未经授权,则应尝试进行X509身份验证。
问题1: Oauth 2和Kerberos无法一起使用。协商请求由 SpnegoAuthenticationProcessingFilter 发起,但协商响应由 OAuth2AuthenticationEntryPoint.commence 方法获取,并引发以下异常
“访问此资源需要完整身份验证”
如果我在安全配置中注释掉OAuth依赖项和相关配置,则其工作正常。
问题2::如果Kerbeors身份验证失败,我该如何处理响应并调用其他身份验证配置?
在下面检查我当前的工作代码:
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Configuration
@Order(2)
public static class PermitUnsecuredEndpoints extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.requestMatchers()
.antMatchers(
"/info",
...
...
"/api-docs/**",
)
.and()
.authorizeRequests()
.anyRequest().permitAll();
}
}
@Configuration
@Order(4)
public static class Kerberos extends WebSecurityConfigurerAdapter {
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.kerberos-conf}")
private String krb5Location;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and().authorizeRequests().anyRequest().authenticated()
.and().formLogin()
.and().addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
BasicAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider());
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@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();
}
@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
filter.setFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
);
filter.setAuthenticationManager(authenticationManager);
return filter;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() throws Exception {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
final GlobalSunJaasKerberosConfig krb5Config = new GlobalSunJaasKerberosConfig();
krb5Config.setKrbConfLocation(krb5Location);
krb5Config.afterPropertiesSet();
ticketValidator.setServicePrincipal(servicePrincipal);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
ticketValidator.setDebug(true);
ticketValidator.afterPropertiesSet();
return ticketValidator;
}
@Bean
public SunJaasKrb5LoginConfig loginConfig() {
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
loginConfig.setUseTicketCache(true);
return loginConfig;
}
@Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
}
@Configuration
@Order(5)
public static class X509Endpoints extends WebSecurityConfigurerAdapter {
private static final String CN_PATTERN = "CN=(.*?)(?:,|$)";
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.authorizeRequests().anyRequest().authenticated().and()
.x509().subjectPrincipalRegex(CN_PATTERN)
.authenticationUserDetailsService(new X509AuthenticatedUserDetailsService());
}
protected static class X509AuthenticatedUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
@Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws UsernameNotFoundException {
X509Certificate certificate = (X509Certificate) token.getCredentials();
return new User(certificate.getSubjectX500Principal().getName(), "", Lists.newArrayList());
}
}
}
/* *
* Configuration for authenticating JWT tokens.
*
* Default order is 3. Can possibly be changed with:
* security:
* oauth2:
* resource:
* filter-order: 3*/
@Configuration
@EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private final static String EMAIL_KEY = "email";
private final ResourceServerProperties resourceServerProperties;
public ResourceServerConfiguration(ResourceServerProperties resourceServerProperties) {
this.resourceServerProperties = resourceServerProperties;
}
@Bean
public TokenStore jwkTokenStore() {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {
@Override
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication) {
Map<String, ?> stringMap = super.convertUserAuthentication(userAuthentication);
return stringMap;
}
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(EMAIL_KEY)) {
String email = map.get(EMAIL_KEY).toString();
User user = new User(email, "N/A", Collections.emptySet());
return new UsernamePasswordAuthenticationToken(user, "N/A", null);
}
return null;
}
@Override
public String toString() {
return "DefaultUserAuthenticationConverter";
}
});
return new JwkTokenStore(this.resourceServerProperties.getJwk().getKeySetUri(), accessTokenConverter);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests()
.anyRequest().authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceServerProperties.getId());
}
}
}