Несколько провайдеров аутентификации в Spring Security
1. обзор
В этой быстрой статье мы сосредоточимся на использовании нескольких механизмов для аутентификации пользователей в Spring Security.
Мы сделаем это, настроив несколько поставщиков аутентификации.
2. Провайдеры аутентификации
AuthenticationProvider - это абстракция для получения информации о пользователе из определенного репозитория (например,database,LDAP,custom third party source и т. Д.) ). Он использует извлеченную информацию о пользователе для проверки предоставленных учетных данных.
Проще говоря, когда определено несколько поставщиков аутентификации, они будут опрашиваться в том порядке, в котором они были объявлены.
Для быстрой демонстрации мы настроим двух провайдеров аутентификации - пользовательского провайдера аутентификации и провайдера аутентификации в памяти.
3. Maven Зависимости
Давайте сначала добавим необходимые зависимости Spring Security в наше веб-приложение:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
А без Spring Boot:
org.springframework.security
spring-security-web
5.0.4.RELEASE
org.springframework.security
spring-security-core
5.0.4.RELEASE
org.springframework.security
spring-security-config
5.0.4.RELEASE
Последнюю версию этих зависимостей можно найти вspring-security-web,spring-security-core иspring-security-config.
4. Пользовательский провайдер аутентификации
Теперь давайте создадим настраиваемого поставщика аутентификации, реализовав интерфейсAuthneticationProvider.
Мы собираемся реализовать методauthenticate, который пытается выполнить аутентификацию. Входной объектAuthentication содержит учетные данные имени пользователя и пароля, предоставленные пользователем.
Методauthenticate возвращает полностью заполненный объектAuthentication, если аутентификация прошла успешно. Если аутентификация не удалась, генерируется исключение типа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);
}
}
Естественно, это простая реализация для целей нашего примера здесь.
5. Настройка нескольких поставщиков аутентификации
Теперь давайте добавимCustomAuthenticationProvider и провайдер аутентификации в памяти в нашу конфигурацию Spring Security.
5.1. Конфигурация Java
В нашем классе конфигурации давайте теперь создадим и добавим поставщиков аутентификации с помощьюAuthenticationManagerBuilder.
СначалаCustomAuthenticationProvider, а затем провайдер аутентификации в памяти с использованиемinMemoryAuthentication().
Мы также гарантируем, что доступ к шаблону URL «/api/**» должен быть аутентифицирован:
@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. Конфигурация XML
В качестве альтернативы, если мы хотим использовать конфигурацию XML вместо конфигурации Java:
6. Приложение
Затем давайте создадим простую конечную точку REST, которая будет защищена двумя нашими поставщиками аутентификации.
Для доступа к этой конечной точке необходимо указать действительные имя пользователя и пароль. Наши провайдеры аутентификации проверит учетные данные и определят, разрешить доступ или нет:
@RestController
public class MultipleAuthController {
@GetMapping("/api/ping")
public String getPing() {
return "OK";
}
}
7. тестирование
Наконец, давайте теперь проверим доступ к нашему защищенному приложению. Доступ будет разрешен только при наличии действительных учетных данных:
@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. Заключение
В этом кратком руководстве мы увидели, как в Spring Security можно настроить несколько поставщиков аутентификации. Мы защитили простое приложение с помощью специального провайдера аутентификации и провайдера аутентификации в памяти.
И мы также написали тесты, чтобы убедиться, что для доступа к нашему приложению требуются учетные данные, которые могут быть подтверждены хотя бы одним из наших провайдеров аутентификации.
Как всегда, полный исходный код реализации можно найти вover on GitHub.