원문: https://spring.io/guides/topicals/spring-security-architecture
본 가이드는 스프링 시큐리티의 기본 지침서이며, 프레임워크의 설계와 기본적인 단위들에 대한 이해를 제공한다. 애플리케이션 보안(security)에 대한 아주 기본적인 내용만 다루지만, 이를 통해 스프링 시큐리티를 사용하며 개발자들이 느낄 수 있는 혼란들을 정리하고자 한다. 이 설명을 통해 우리는 필터들과 좀 더 일반적인 메소드 어노테이션을 통해 웹 애플리케이션에 시큐리티를 적용하는 방법을 살펴볼 것이다. 애플리케이션 보안이 이루어지는 방식과 커스터마이즈하는 법을 더 높은 수준으로 이해할 필요가 있거나, 단순히 애플리케이션 보안에 대해 배우고 싶을 때 본 가이드를 활용하면 된다.
본 가이드는 가장 기본적인 문제 이외의 문제들을 해결하기 위한 매뉴얼로써 만들어진 것이 아니다(그런 것들을 위한 소스는 따로 있다). 하지만 초보자나 전문가 모두에게 유용할 것이다. 스프링 부트는 매우 많이 언급될텐데, 스프링 부트는 기본적인 애플리케이션 보안을 제공하며 그것이 전체적인 아키텍쳐에 어떻게 부합하는지 이해하는 데에 유용하기 때문이다. 모든 원리들은 스프링 부트를 사용하지 않는 애플리케이션에도 똑같이 잘 적용될 것이다.
인증과 접근 제어(Authentication and Access Control)
애플리케이션 보안은 두 가지 정도의 독립된 문제들로 귀결된다. 인증(너 누구니?, authentication)과 권한(너 뭐 할 수 있니?, authorization, 인가)이다. 가끔 사람들은 "권한(authorization)"을 "접근 제어(access control)"라고 말할 때가 있는데 매우 헷갈린다. 하지만 그냥 둘은 같은거라고 생각하는게 편할 것이다. 왜냐하면 "권한"은 여러 군데에서 오버로딩되기 때문이다. 스프링 시큐리티는 권한으로부터 인증이 분리된 아키텍쳐를 가지고 있으며, 그 두 가지 모두에 대해 다양한 전략과 확장이 가능하다.
인증(Authentication)
인증의 주요한 전략 인터페이스는 메소드를 하나만 가지고 있는 'AuthenticationManager'이다.
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
AuthenticationManager는 authenticate() 메소드로 세 가지 일을 할 수 있다.
- input이 '유효한 주체(valid principal)'로 확인되면 'Authentication'을 반환한다(보통 'authenticated=true'와 함께).
- input이 유효하지 않은 주체인 것 같을 때 'AuthenticationException'을 발생시킨다.
- 결정할 수 없을 때 'null'을 반환한다.
AuthenticationException은 런타임 예외다. 애플리케이션의 스타일과 목적에 달려있긴 하지만, 보통 이 예외는 일반적인 방법으로 처리된다. 즉, 사용자가 작성한 코드는 보통 이 예외를 캐치하거나 처리하지 않는다. 예를 들어, 인증이 실패했다는 웹 UI 페이지가 뜨면, 백엔드 HTTP 서비스에서는, context에 따라 'WWW-Authenticate' 헤더를 첨부하거나 하지 않은 채로 401을 반환할 것이다.
가장 흔하게 쓰는 AuthenticationManager의 구현체는 'ProviderManager'이다. ProviderManager는 AuthenticationProvider 인스턴스 chain에 위임하는 역할을 한다. AuthenticationProvider는 AuthenticationManager와 약간 비슷하지만 추가적인 메소드를 가지고 있는데, 이 메소드는 주어진 Authentication 타입을 호출자가 지원(support)하는 경우 호출자가 쿼리를 할 수 있도록 한다.
public interface AuthenticationProvider {
Authentication authentication(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
}
supports() 안의 Class<?> 매개변수는 실제로 'Class<? extends Authentication>'이다(이 메소드는 클래스가 authenticate() 메소드를 통과할 수 있는지 여부를 물을때만 호출된다). ProviderManager는 AuthenticationProviders의 chain으로의 위임을 통해, 같은 애플리케이션 안에서 다양한 여러 인증 메커니즘들을 지원한다. 만약 ProviderManager가 특정 Authentication 인스턴스 타입을 알아보지 못하면 그건 그냥 넘어간다.
(미완성)