1. Información general
En este artículo rápido, nos centraremos en el uso de múltiples mecanismos para autenticar a los usuarios en Spring Security.
Lo haremos configurando varios proveedores de autenticación.
2. Proveedores de autenticación
Un AuthenticationProvider es una abstracción para obtener información del usuario de un repositorio específico (como una base de datos, LDAP, fuente de terceros personalizada, etc.). Utiliza la información de usuario obtenida para validar las credenciales proporcionadas.
En pocas palabras, cuando se definen varios proveedores de autenticación, se consultará a los proveedores en el orden en que se declaran.
Para una demostración rápida, configuraremos dos proveedores de autenticación: un proveedor de autenticación personalizado y un proveedor de autenticación en memoria.
3. Dependencias de Maven
Primero agreguemos las dependencias necesarias de Spring Security en nuestra aplicación web:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security
Y, sin Spring Boot:
org.springframework.security spring-security-web 5.2.2.RELEASE org.springframework.security spring-security-core 5.2.2.RELEASE org.springframework.security spring-security-config 5.2.2.RELEASE
La última versión de estas dependencias se puede encontrar en spring-security-web, spring-security-core y spring-security-config.
4. Proveedor de autenticación personalizado
Ahora creemos un proveedor de autenticación personalizado implementando la interfaz AuthneticationProvider .
Vamos a implementar el método de autenticación , que intenta la autenticación. El objeto de autenticación de entrada contiene las credenciales de nombre de usuario y contraseña proporcionadas por el usuario.
El método de autenticación devuelve un objeto de autenticación completamente poblado si la autenticación es exitosa. Si la autenticación falla, lanza una excepción de tipo AuthenticationException :
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication auth) throws AuthenticationException { String username = auth.getName(); String password = auth.getCredentials() .toString(); if ("externaluser".equals(username) && "pass".equals(password)) { return new UsernamePasswordAuthenticationToken (username, password, Collections.emptyList()); } else { throw new BadCredentialsException("External system authentication failed"); } } @Override public boolean supports(Class auth) { return auth.equals(UsernamePasswordAuthenticationToken.class); } }
Naturalmente, esta es una implementación simple para el propósito de nuestro ejemplo aquí.
5. Configuración de varios proveedores de autenticación
Ahora agreguemos CustomAuthenticationProvider y un proveedor de autenticación en memoria a nuestra configuración de Spring Security.
5.1. Configuración de Java
En nuestra clase de configuración, ahora creemos y agreguemos los proveedores de autenticación usando AuthenticationManagerBuilder .
Primero, CustomAuthenticationProvider y luego, un proveedor de autenticación en memoria mediante inMemoryAuthentication () .
También nos aseguramos de que el acceso al patrón de URL " / api / ** " deba estar autenticado:
@EnableWebSecurity public class MultipleAuthProvidersSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired CustomAuthenticationProvider customAuthProvider; @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthProvider); auth.inMemoryAuthentication() .withUser("memuser") .password(encoder().encode("pass")) .roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http.httpBasic() .and() .authorizeRequests() .antMatchers("/api/**") .authenticated(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
5.2. Configuración XML
Alternativamente, si queremos usar la configuración XML en lugar de la configuración Java:
6. La aplicación
A continuación, creemos un punto final REST simple que esté protegido por nuestros dos proveedores de autenticación.
Para acceder a este punto final, se debe proporcionar un nombre de usuario y una contraseña válidos. Nuestros proveedores de autenticación validarán las credenciales y determinarán si permiten el acceso o no:
@RestController public class MultipleAuthController { @GetMapping("/api/ping") public String getPing() { return "OK"; } }
7. Prueba
Finalmente, ahora probemos el acceso a nuestra aplicación segura. Se permitirá el acceso solo si se proporcionan credenciales válidas:
@Autowired private TestRestTemplate restTemplate; @Test public void givenMemUsers_whenGetPingWithValidUser_thenOk() { ResponseEntity result = makeRestCallToGetPing("memuser", "pass"); assertThat(result.getStatusCodeValue()).isEqualTo(200); assertThat(result.getBody()).isEqualTo("OK"); } @Test public void givenExternalUsers_whenGetPingWithValidUser_thenOK() { ResponseEntity result = makeRestCallToGetPing("externaluser", "pass"); assertThat(result.getStatusCodeValue()).isEqualTo(200); assertThat(result.getBody()).isEqualTo("OK"); } @Test public void givenAuthProviders_whenGetPingWithNoCred_then401() { ResponseEntity result = makeRestCallToGetPing(); assertThat(result.getStatusCodeValue()).isEqualTo(401); } @Test public void givenAuthProviders_whenGetPingWithBadCred_then401() { ResponseEntity result = makeRestCallToGetPing("user", "bad_password"); assertThat(result.getStatusCodeValue()).isEqualTo(401); } private ResponseEntity makeRestCallToGetPing(String username, String password) { return restTemplate.withBasicAuth(username, password) .getForEntity("/api/ping", String.class, Collections.emptyMap()); } private ResponseEntity makeRestCallToGetPing() { return restTemplate .getForEntity("/api/ping", String.class, Collections.emptyMap()); }
8. Conclusión
En este tutorial rápido, hemos visto cómo se pueden configurar varios proveedores de autenticación en Spring Security. Hemos asegurado una aplicación simple utilizando un proveedor de autenticación personalizado y un proveedor de autenticación en memoria.
Y también hemos escrito pruebas para verificar que el acceso a nuestra aplicación requiere credenciales que puedan ser validadas por al menos uno de nuestros proveedores de autenticación.
Como siempre, el código fuente completo de la implementación se puede encontrar en GitHub.