1. 개요
이 빠른 기사에서는 Spring Security에서 사용자를 인증하기 위해 여러 메커니즘을 사용하는 데 중점을 둘 것입니다.
여러 인증 공급자를 구성하여 이를 수행합니다.
2. 인증 공급자
AuthenticationProvider 는 특정 저장소(예: 데이터베이스 , LDAP , 사용자 지정 타사 소스 등) 에서 사용자 정보를 가져오기 위한 추상화입니다 . 가져온 사용자 정보를 사용하여 제공된 자격 증명의 유효성을 검사합니다.
간단히 말해 여러 인증 공급자가 정의되면 공급자는 선언된 순서대로 쿼리됩니다.
빠른 데모를 위해 사용자 지정 인증 공급자와 메모리 내 인증 공급자라는 두 가지 인증 공급자를 구성합니다.
3. 메이븐 의존성
먼저 필요한 Spring Security 의존성을 웹 애플리케이션에 추가해 보겠습니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
그리고 스프링 부트 없이:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
이러한 의존성의 최신 버전은 spring-security-web , spring-security-core 및 spring-security-config 에서 찾을 수 있습니다 .
4. 사용자 지정 인증 제공자
이제 AuthneticationProvider 인터페이스 를 구현하여 사용자 지정 인증 공급자를 만들어 보겠습니다 .
인증 을 시도하는 인증 방법 을 구현할 것 입니다. 입력 인증 개체에는 사용자가 제공한 사용자 이름 및 암호 자격 증명이 포함되어 있습니다.
인증 방법 은 인증에 성공하면 완전히 채워진 인증 개체를 반환합니다. 인증에 실패하면 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. 자바 구성
구성 클래스에서 이제 AuthenticationManagerBuilder 를 사용하여 인증 공급자를 만들고 추가해 보겠습니다 .
먼저 CustomAuthenticationProvider 를 사용한 다음 inMemoryAuthentication() 을 사용하여 메모리 내 인증 공급자를 만듭니다.
또한 URL 패턴 " /api/** "에 대한 액세스가 인증되어야 하는지 확인하고 있습니다.
@EnableWebSecurity
public class MultipleAuthProvidersSecurityConfig {
@Autowired
CustomAuthenticationProvider customAuthProvider;
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(customAuthProvider);
authenticationManagerBuilder.inMemoryAuthentication()
.withUser("memuser")
.password(passwordEncoder().encode("pass"))
.roles("USER");
return authenticationManagerBuilder.build();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authManager)
throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.authenticationManager(authManager);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5.2. XML 구성
또는 Java 구성 대신 XML 구성을 사용하려는 경우:
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="memuser" password="pass"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
<security:authentication-provider
ref="customAuthenticationProvider" />
</security:authentication-manager>
<security:http>
<security:http-basic />
<security:intercept-url pattern="/api/**"
access="isAuthenticated()" />
</security:http>
6. 신청
다음으로 두 인증 공급자가 보호하는 간단한 REST 엔드포인트를 생성해 보겠습니다.
이 Endpoints에 액세스하려면 유효한 사용자 이름과 암호를 제공해야 합니다. 인증 공급자는 자격 증명을 확인하고 액세스 허용 여부를 결정합니다.
@RestController
public class MultipleAuthController {
@GetMapping("/api/ping")
public String getPing() {
return "OK";
}
}
7. 테스트
마지막으로 Security 애플리케이션에 대한 액세스를 테스트해 보겠습니다. 유효한 자격 증명이 제공된 경우에만 액세스가 허용됩니다.
@Autowired
private TestRestTemplate restTemplate;
@Test
public void givenMemUsers_whenGetPingWithValidUser_thenOk() {
ResponseEntity<String> result
= makeRestCallToGetPing("memuser", "pass");
assertThat(result.getStatusCodeValue()).isEqualTo(200);
assertThat(result.getBody()).isEqualTo("OK");
}
@Test
public void givenExternalUsers_whenGetPingWithValidUser_thenOK() {
ResponseEntity<String> result
= makeRestCallToGetPing("externaluser", "pass");
assertThat(result.getStatusCodeValue()).isEqualTo(200);
assertThat(result.getBody()).isEqualTo("OK");
}
@Test
public void givenAuthProviders_whenGetPingWithNoCred_then401() {
ResponseEntity<String> result = makeRestCallToGetPing();
assertThat(result.getStatusCodeValue()).isEqualTo(401);
}
@Test
public void givenAuthProviders_whenGetPingWithBadCred_then401() {
ResponseEntity<String> result
= makeRestCallToGetPing("user", "bad_password");
assertThat(result.getStatusCodeValue()).isEqualTo(401);
}
private ResponseEntity<String>
makeRestCallToGetPing(String username, String password) {
return restTemplate.withBasicAuth(username, password)
.getForEntity("/api/ping", String.class, Collections.emptyMap());
}
private ResponseEntity<String> makeRestCallToGetPing() {
return restTemplate
.getForEntity("/api/ping", String.class, Collections.emptyMap());
}
8. 결론
이 빠른 사용방법(예제)에서는 Spring Security에서 여러 인증 공급자를 구성하는 방법을 살펴보았습니다. Custom형 인증 공급자와 메모리 내 인증 공급자를 사용하여 간단한 애플리케이션을 보호했습니다.
또한 애플리케이션에 액세스하려면 인증 제공자 중 최소 한 명이 검증할 수 있는 자격 증명이 필요한지 확인하는 테스트를 작성했습니다.
항상 그렇듯이 구현의 전체 소스 코드는 GitHub 에서 찾을 수 있습니다 .