스프링의 공식 가이드를 해석한 글입니다.
영어가 많이 부족하기 때문에 내용의 생략과 오류가 있을 수 있습니다.
출처 https://spring.io/guides/topicals/spring-security-architecture
애플리케이션 보안은 대략 두개의 독립적인 문제로 요약할 수 있다.
인증 (당신은 누군가?) 과 인가 (어떤 행위를 할 수 있도록 허가받았는가?) 이다.
가끔은 "인가" 대신 "접근 제어" 로 불리기도 한다
스프링 시큐리티는 인증을 인가로부터 분리하도록 디자인된 구조를 갖고 있고
둘의 전략과 extension point(?)를 갖는다.
인증을 위한 주요 인터페이스는 AuthenticationManager 이다.
하나의 메서드만을 갖는다.
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
AuthenticationManager는 authenticat() 메서드를 통해
3가지 일을 할 수 있다.
입력이 타당하면 Authentication(보통 authenticated=true와 함께)
객체를 반환한다.
입력이 타당하지 않다고 판단되면 AuthenticationException을 반환한다.
판단할 수 없으면 null을 반환한다.
AuthenticationException은 런타임 예외이다.
애플리케이션의 목적과 스타일에 따라 일반적인 방식으로 애플리케이션에게 처리된다.
다르게 말하면, 개발자의 코드는 보통 이것을 catch하거나 처리하지 않는다.
(해석이 모호한 부분)
예를 들어 한 UI가 인증이 실패했다는 페이지를 렌더링하고
WWW-Authenticate 헤더와 상관 없이 context에 따하
백엔드 HTTP 서비스에서는 401을 응답한다.
(해석이 모호한 부분)
AuthenticationManager 의 가장 흔하게 사용되는 구현체는 ProviderManager이다.
이것은 여러개의 AuthenticationProvider 객체에게 위임(delegation)한다.
AuthenticationProvider는 AuthenticationManager와 약간 비슷하다.
하지만 호출자가 이것이 주어진 Authintication 타입을 지원하는지
질의할 수 있는 추가적인 메서드를 갖고있다.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
supports() 메서드의 Class<?> 파라미터는
사실 Class<? extends Authentication>이다.
(authenticate() 메서드를 통해 받은 것만에 대해 지원하는지 질의 받는다.)
ProviderManager는 여러개의 AuthenticationProvider들에게 위임하는 것을 통해
다양한 인증 메커니즘을 지원할 수 있다.
ProviderManager가 특정한 Authentication 타입을 인식하지 못하면
스킵된다.
ProviderManager는 모든 ProviderManager가 null을 반환하는지
알 수 있는 선택적인 부모를 갖는다.
부모가 없으면 null인 Authentication은 AuthenticationException을 던진다.
가끔은 애플리케이션이 보호된 자원의 논리적 그룹들을 갖는다.
(예를 들어 같은 경로 패턴을(/api/**와 같은) 갖는 모든 웹 자원)
각각의 그룹은 자신의 전용 AuthenticationManager을 가질 수 있다.
보통은 ProviderManager을 갖고 같은 부모를 공유한다.
부모는 일종의 "global" 자원이고 모든 provider들의 fallback 역할을 한다.
(fallback이란 어떤 기능이 제대로 동작하지 않을때 이에 대처하는 기능이나 동작)
스프링 시큐리티는 공통의 authentication manager 의 특징의 셋업을
빠르게 할 수 있도록 도와주는 설정 도우미가 있다.
가장 흔히 쓰이는 도우미는 AuthenticationManagerBuilder이다.
in-memory,JDBC, LDAP 유저 디테일이나
커스텀 UserDetailsService을 추가하는데 훌륭하다.
아래의 예제는 gloabl(부모) AuthenticationManager을
설정하는 애플리케이션을 보여준다.
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
... // web stuff here
@Autowired
public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}
}
이 예제는 웹 애플리케이션과 연관돼있다.
하지만 이런 AuthenticationManagerBuilder의 사용은 더 넓게 응용할 수 있다.
(웹 보안이 어떻게 구현되는지는 https://spring.io/guides/topicals/spring-security-architecture#web-security 을 봐라)
AuthenticationManagerBuilder 가 메서드 안에 스프링 빈으로 인식돼
@Autowired(주입)됐는지 확인하자. 이게 glabal(부모) AuthenticationManager을 만든다.
반대로, 아래의 예제를 보자.
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
... // web stuff here
@Override
public void configure(AuthenticationManagerBuilder builder) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}
}
configurer의 @Override 메서드를 사용하면,
AuthenticationManagerBuilder 는 글로벌 부모의 하나의 자손인 "로컬" AuthenticationManager 을 만들기 위해서만 쓰일 것이다.
스프링 부트 애플리케이션에서 글로벌 매니저를 또다른 빈에 주입할 수 있다.
하지만 스스로 명백하게 노출시키지(?) 않는 한 로컬 매니저는 다른 빈에 주입 할 수 없다.
따로 AuthenticationManager 타입의 빈을 만들지 않는 한,
스프링 부트는 기본 글로벌 AuthenticationManager을 제공한다.
기본으로 제공되는건 걱정 할 필요 없이 충분히 안전하다.
AuthenticationManager을 빌드하는 어떤 설정을 만들려면
보통 기본으로 제공되는 AuthenticationManager에 대해 고려하지 않아도
지역적으로 보호하려는 자원에 할 수 있다.