Spring Security에 관하여
Spring security에 대한 깊은 이해없이 사용하였다. 그래서 Spring security 공식 문서를 읽고 기초를 다지려 하였다.
Spring security를 사용해봐서 알겠지만, 우리가 필요한 부분만 작성할 수 있도록 구조를 잘 짜놓았다. 유연하면서 다양한 authentication과 authorization을 제공한다. (나도 코드를 작성할 때 Spring Security 구조처럼 OCP를 지키도록 해야지...)
- secure application이 어떻게 동작하는지?
- 어떻게 커스터마이징하는지?
- application의 보안에 대한 고민이 필요할 때
사실 spring boot에서 워낙 auto configuration이 잘 되어 있어서 해당 문서를 읽지 않고도 충분히 구현이 가능하다.
하지만, 전체적인 아키텍쳐가 궁금하여 이 문서를 읽게되었다.
Authentication and Access Control ( 인증 및 접근 제어 )
쉽게 말하면 '누구냐?'와 '너한테 권한있니?' 이 2가지만 생각하면 된다.
Authentication 객체?
- 이름
- 권한
- 인증여부
Authentication 인증 과정
인증과 관련된 핵심 interface는 아래 인터페이스다.
authenticate() 메소드는 3가지 행동을 할 수 있다.
- 유효한 본인(valid principal)을 나타내는 input임을 증명한 경우 Authentication 객체를 리턴한다.
- input이 잘못된 본인을 나타내면 AuthenticationException을 리턴한다.
- 결정할 수 없다면 null을 리턴한다.
가장 많이 사용되는 AuthenticationManager 구현체는 ProviderManager이다.
ProviderManager는 AuthenticationProvider chain에게 위임을 한다. 이를 통해 다양한 여러가지의 인증 메카니즘을 가질 수 있게 해준다. AuthenticationProvider는 AuthenticationManager와 비슷한 개념이지만, Authentication 타입을 지원하는지 체크도 해준다.
- /api/** 와 같이 논리적인 그룹에 AuthenticationManager를 적용할 수도 있다.
- "Global"로 전체에 적용도 가능하다.
DaoAuthenticationProvider에서는 UserDetailsService를 주입 받아 비밀번호 대조 및 회원 정보 가져오기를 수행한다. 여기서도 DI의 위대함을 볼 수 있다. Spring Security에서는 자세한 인증 및 조회를 제외하고는 뼈대를 잘 갖춰 놓은 것이다.
인증 매니저 커스터마이징 하기 ( Customizing Authentication Managers )
스프링 시큐리티는 Configuration helper를 제공해준다. 이는 어플리케이션에 일반적인 인증 매니저 특징을 가질 수 있도록 해준다.
AuthenticationManagerBuilder 유저정보를 in-memory, JDBC, LDAP에 설정하는 걸 도와주고, 커스텀 UserDetailsService를 더할 수 있게 해준다.
스프링 부트에서 기본적으로 global AuthenticationManager를 제공해주기 때문에 걱정하지 않아도 된다.
Authorization or Access Control ( 접근 권한 제어 )
접근과 관련된 핵심은 AccessDecisionManager에 있다. 이 클래스는 AccessDecisionVoter에게 위임을 한다
Web Security
web tier에 있는 Spring Security는 Servlet Filter에 기반을 두고 있다.
아래 그림은 HTTP request를 위한 handler layer를 나타내는 것이다.
클라이언트에서 app에 요청을 보내면, container는 요청 url에 따라 어떠한 filter나 servlet을 적용할지 정한다. 보통 하나의 request당 하나의 servlet을 쓰지만, filter는 여러개가 적용이 가능하다. 그렇기 때문에 순서도 중요하게 된다.
Spring Boot에서 제공해주는 방법
- Filter type의 Bean에는 @Order 어노테이션으로 순서를 정할 수 있다.
- FilterRegistrationBean을 이용하여 순서를 정할 수 있다
Spring Security도 Filter에 등록 되어 있다. FilterChainProxy라는 클래스로 등록 되어 있다. 하나의 필터로 등록 되어있지만 내부적으로는 여러개의 Filter가 동작하고 있다.
기본적으로 11개의 filter로 구성된 chain으로 이루어져 있다. 우리가 이게 어떤 순서로 호출되는지 어떻게 사용되는지에 대해서는 알 필요가 없다. ( authentication, authorization, exception handling, session handling, header writing 등등 )
Filter Chain 만들기, 커스터마이징하기
Spring boot에서 기본적으로 설정한 filter 순서는 SecurityProperties.BASIC_AUTH_ORDER에 정의되어 있다.
security.basic.enabled=false 설정을 통해 기본설정을 없앨 수도 있고, 다른 룰을 더 우선순위에 둘 수도 있다.
@Order에 순서를 정의하여 아래처럼 할 수 있다.
많은 애플리케이션은 서로 다른 resource 접근 규칙을 가지고 있다.
예를 들어, cookie-based 인증을 통해 로그인 페이지로 리다이렉트를 지원하지만, token-based 인증은 401 응답을 하는 경우가 있다. 각각의 리소스들은 WebSecurityConfigurerAdapter를 가지고 있는데, 여기에는 순서와 각각의 request에 맞는 필터들을 가지고 있다. 만약에 순서가 더 위에 있다면 이게 우선순위로 적용이 된다.
Filter 만들어 보기 - JWT 검증하는 필터
JWT로 인증을 구현하게 된다면 여러가지 방법이 있겠지만
보통 헤더에 " Authorization : Bearer token...." 과 같은 형태로 보내준다. 여기에는 인증된 회원정보가 들어있다.
doFilterInternal()에서 토큰 검증을 한 후에 payload에 담긴 회원 정보를 가지고 와서 회원을 조회하면 된다.
인증된 정보를 아래와 같이 설정해주면 된다.
// set current authenticated user
SecurityContextHolder.getContext()
.setAuthentication(createAuthentication(userDetails, request));
Threads와 작업하기
Spring security는 현재 인증된 사용자에 대한 정보를 만들어야 하기 때문에 thread bound 되도록 설계가 되어있다.
만약에 Web Endpoint에서 현재 인증된 user를 가지고 오고 싶다면, 아래와 같이 사용하면 된다. SecurityContext에서 Authentication을 가져온다음에 getPrincipal() 함수를 호출해준다.
Spring Security가 사용되지 않을때 아래와 같이 사용하면 된다. HttpServletRequest로 부터 Principal을 가져온다면 type은 Authentication이 될 것이고 아래와 같이 사용하면 된다.
비동기로 Secure Method 처리하기
SecurityContext는 thread bound이기 때문에, @Async랑 같이 secure method를 호출하기 원한다면, context가 잘 전파(propagated)되는지 확인해야 한다. 백그라운드에서 실행되는 task(Runnable, Callable etc)와 SecurityContext를 합쳐줘야 한다.
Spring Security에서는 @Async 함수에 SecuirtyContext가 전파되도록 도와준다. 아래와 같이 설정을 하면 된다.
참고 : https://spring.io/guides/topicals/spring-security-architecture
'Back-End > Spring' 카테고리의 다른 글
Spring MVC에 관하여 (0) | 2019.06.07 |
---|---|
Spring layered architecture와 객체지향적으로 개발하기 (0) | 2019.05.31 |
Spring boot 2.0에 관하여 (0) | 2019.05.20 |
스프링 CORS (0) | 2019.05.20 |
스프링 5 - PushBuilder란 (0) | 2019.05.07 |