У меня есть два метода аутентификации в моем REST WebService:
- Внутренний единый вход
- Сертификат (X.509)
Каждый изэтот метод защищает разные конечные точки, но теперь мне нужно, чтобы одна конечная точка отвечала за оба метода, если первый отказывает, он вызывает второй (и наоборот).Возможно ли это в Spring Security?
Вот мой код безопасности для обоих методов:
@Configuration
@Order(1)
public static class X509WebSecurityConfig extends WebSecurityConfigurerAdapter {
private SecurityUtil securityUtil;
@Autowired
public X509WebSecurityConfig(SecurityUtil securityUtil) {
this.securityUtil = securityUtil;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/cert/**")
.authorizeRequests()
.anyRequest().hasRole("X509");
http.x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").authenticationUserDetailsService(preAuthenticatedX509());
}
@Bean
public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthenticatedX509() {
return token -> {
if ("MYCERTCN".equalsIgnoreCase(token.getName())) {
return securityUtil.createUserFromCertificate();
}
throw new UsernameNotFoundException("Could not validate CN provided for X.509 authentication. CN provided: " + token.getName());
};
}
}
@Configuration
@Order(2)
public static class GeneralWebSecurityConfig extends WebSecurityConfigurerAdapter {
private CustomAccessDeniedHandler customAccessDeniedHandler;
private UserServiceAuth userService;
@Autowired
public GeneralWebSecurityConfig(CustomAccessDeniedHandler customAccessDeniedHandler,
UserServiceAuth userService) {
this.customAccessDeniedHandler = customAccessDeniedHandler;
this.userService = userService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/**").hasAnyRole(ApplicationRole.REGULAR_USER.name());
http.authorizeRequests().antMatchers("/error").permitAll();
http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.jee()
.authenticatedUserDetailsService(userService)
.addObjectPostProcessor(new ObjectPostProcessor<J2eePreAuthenticatedProcessingFilter>() {
@Override
public J2eePreAuthenticatedProcessingFilter postProcess(J2eePreAuthenticatedProcessingFilter filter) {
return new MyUserFilter(http);
}
});
http.csrf().disable();
}
}
private static class MyUserFilter extends J2eePreAuthenticatedProcessingFilter {
MyUserFilter(HttpSecurity http) {
setAuthenticationDetailsSource(createWebAuthenticationDetailsSource());
setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getUserPrincipal();
}
private J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource createWebAuthenticationDetailsSource() {
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource detailsSource = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
SimpleMappableAttributesRetriever rolesRetriever = new SimpleMappableAttributesRetriever();
rolesRetriever.setMappableAttributes(Collections.emptySet());
detailsSource.setMappableRolesRetriever(rolesRetriever);
return detailsSource;
}
}
Это то, что у меня сегодня и отлично работает для префиксов отдельных конечных точек.
Но теперь я хотел бы что-то вроде этого:
@Configuration
@Order(3)
public static class BothWebSecurityConfig extends WebSecurityConfigurerAdapter {
private CustomAccessDeniedHandler customAccessDeniedHandler;
private UserServiceAuth userService;
private SecurityUtil securityUtil;
@Autowired
public BothWebSecurityConfig(CustomAccessDeniedHandler customAccessDeniedHandler,
UserServiceAuth userService,
SecurityUtil securityUtil) {
this.customAccessDeniedHandler = customAccessDeniedHandler;
this.userService = userService;
this.securityUtil = securityUtil;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/both/**")
.authorizeRequests()
.anyRequest().hasRole(ApplicationRole.X509.name());
http.x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").authenticationUserDetailsService(preAuthenticatedX509Both());
http.antMatcher("/both/**").authorizeRequests().anyRequest().hasAnyRole(ApplicationRole.REGULAR_USER.name());
http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.jee()
.authenticatedUserDetailsService(userService)
.addObjectPostProcessor(new ObjectPostProcessor<J2eePreAuthenticatedProcessingFilter>() {
@Override
public J2eePreAuthenticatedProcessingFilter postProcess(J2eePreAuthenticatedProcessingFilter filter) {
return new MyUserFilter(http);
}
});
http.csrf().disable();
}
@Bean
public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthenticatedX509Both() {
return token -> {
if ("MYCERTCN".equalsIgnoreCase(token.getName())) {
return securityUtil.createUserFromCertificate();
}
throw new UsernameNotFoundException("Could not validate CN provided for X.509 authentication. CN provided: " + token.getName());
};
}
}
Но вышеупомянутое игнорирует безопасность X509 и всегда идет ко второму способу (JEE).
Можно защитить то же самоеконечная точка с двумя различными методами, X.509 и JEE (если один не удается перейти к другому и наоборот?).