🙋 들어가며
이번 글에서는 시큐리티가 어떻게 초기화되고 어떤 값들이 생성되는지 알아보겠습니다.
시큐리티는 여러 개의 필터 체인을 관리할 수 있는데 어떻게 설정하는지 알아보겠습니다.
그리고 요청이 어떻게 알맞은 필터 체인에 타고 흐르는지도 알아보겠습니다.
📁 설정 파일 작성 (feat. Security 6)
시큐리티를 사용하기 위해서는 우선 설정 파일을 작성해야 합니다.
다음과 같이 단 어노테이션 하나만 달아주면 기본적인 시큐리티 설정이 완료됩니다.
@Configuration
@EnableWebSecurity // 기본 시큐리티 설정
public class SecurityConfig {
}
바로 @EnableWebSecurity
입니다. 전혀 복잡하지 않죠? 😋
단순히 달아주기만 하면 애플리케이션이 메모리에 올라올 때 다양한 설정을 해줍니다.
그러면 어떻게 초기화되고 어떤 게 만들어질까요?
📈 초기화 과정
스프링 부트 환경에서 애플리케이션을 시작하면 가장 먼저 내장 서버(Tomcat)가 올라옵니다.
그 뒤로 내부적으로 필요한 Bean들을 생산하는데 그 중에 securityFilterChain
이 존재합니다.
다음과 같이 BeanFactory에서 이름으로 빈을 등록하는 빈을 생성합니다.
이후 ConstructorResolver에서 SecurityFilterAutoConfiguration
을 생성합니다.
이후 AutoConfiguration에서 DelegatingFilterProxy
를 생성해줍니다.
여기서 우리가 설정한 내용에 따라 추가적으로 빈이 생성됩니다. (@ConditionalOnClass)
AbstractSecurityWebApplicationInitializer
는 DelegatingFilterProxy를 등록합니다.
고로 AutoConfiguration에서 빈을 생성하고, Initializer에서 DelegatingFilterProxy가 등록시켜 다른 필터보다
먼저 springSecurityFilterChain을 사용할 수 있게 합니다.
마지막으로 WebSecurityConfiguration
에서 우리가 설정한 필터를 등록됩니다.
여기서 이렇게 반복문을 돌면서 등록하기 때문에 다중 필터 체인을 사용할 수 있습니다.
🛡️ 다양한 보안기능
@EnableWebSecurity
로 간단하지만 다양한 것들을 지원합니다.
지원하는 것들은 다양한데 대표적인 몇 가지만 적어보겠습니다.
- URL에 대한 인증 처리
- 기본적인 로그인/로그아웃 지원
- 다양한 웹 공격 방어 : CRSF 공격, 세션 고정 공격 등
사실 이 어노테이션은 서블릿 필터를 만드는 역할입니다.
바로 이전 글에서 봤던 springSecurityFilterChain
입니다.
여기서 시큐리티가 제공하는 모든 보안은 필터를 통해서 이루어진다는 것을 알 수 있습니다.
따로 설정하지 않으면 어떤 것들이 생성될까요? FilterChainProxy
에 디버그를 걸고 동작시켜 보겠습니다.
다음과 같이 15개의 필터들이 기본적으로 생성됩니다. 필요에 따라 필터를 더 추가할 수도, 없앨 수도 있습니다.
사실 @EnableWebSecurity
이 없더라도 Security Dependency가 추가되어 있다면 자동으로 생성합니다.
주석 처리를 하고 애플리케이션을 동작시켜 보겠습니다.
그래도 다음과 같이 정상적으로 애플리케이션에 올라오는 것을 알 수 있습니다.
⛓️ 다중 필터 체인
이렇게 보안을 관리하는 필터 체인을 여러 개 등록해서 사용할 수 있습니다.
우선 예시를 위해서 다음과 같이 필터 체인을 등록해 보겠습니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// Web attack
http.csrf().disable()
.headers().frameOptions().disable();
// Session management
htt.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
// Login
http.formLogin()
.permitAll();
// Logout
http.logout()
.deleteCookies("remember-me", "JSESSIONID")
.permitAll();
// Remember Me
http.rememberMe()
.key("deep-dive-security")
.rememberMeParameter("remember-me")
.tokenValiditySeconds(TOKEN_VALID_TIME);
// Authentication
http.authorizeHttpRequests()
.requestMatchers("/user").hasRole("USER")
.requestMatchers("/sys").hasRole("SYS")
.requestMatchers("/admin").hasRole("ADMIN");
// Authorization
http.authorizeHttpRequests()
.requestMatchers("/join", "/exception", "/denied")
.permitAll();
return http.build();
}
그리고 같은 설정 파일 안에서 다른 필터 체인을 설정해 보겠습니다.
@Bean
public SecurityFilterChain adminFilter(HttpSecurity http) throws Exception {
http
.securityMatcher("/admin/**")
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
);
http.formLogin(withDefaults());
return http.build();
}
그리고 아까와 같이 FilterChainProxy
디버그를 걸고 동작시켜 보겠습니다.
FilterChainProxy는 내부적으로 필터 체인에 대한 리스트를 가지고 있으므로 this를 확인하면 될 것 같습니다.
public class FilterChainProxy extends GenericFilterBean {
// ...
private List<SecurityFilterChain> filterChains;
// ...
}
아무 곳에 디버그를 걸고 가지고 있는 값을 확인해 주면 다음과 같이 잘 등록된 것을 확인할 수 있습니다.
첫 번째는 2번째로 등록한 필터 체인으로 보입니다.
🤔 어떻게 사용되죠?
앞서 언급했듯이 FilterChainProxy는 모든 필터 체인을 가지고 있습니다.
간단하게 반복문을 사용해서 요청이 들어왔을 때 일치하는 필터 체인을 찾습니다.
이후 요청을 해당 필터에 흘려보냅니다.
그림으로 표현하면 다음과 같습니다. 😋
😋 정리
- 시큐리티는 다양한 보안을 지원한다.
- @EnableWebSecurity로 등록할 수 있다.
- 사실 없어도 애플리케이션에 dependency가 존재하면 기본 설정이 된다.
- 초기화 과정은 복잡하다.
- 필터 체인을 여러 개 등록할 수 있다.
- FilterChainProxy에서 내부적으로 반복문으로 요청에 맞는 필터 체인을 찾는다.
-Reference:
https://docs.spring.io/spring-security/reference/servlet/architecture.html
😋 지극히 개인적인 블로그지만 댓글과 조언은 제 성장에 도움이 됩니다 😋
'Web > Spring-Security' 카테고리의 다른 글
[Security] 시큐리티 파헤치기 (4) : 인증이란? (feat. Authentication) (1) | 2023.08.17 |
---|---|
[Security] 시큐리티 파헤치기 (2) : RequestCacheAwareFilter, RequestCache (2) | 2023.08.02 |
[Security] 시큐리티 파헤치기 (1) : DelegatingFilterProxy (0) | 2023.08.02 |
[Security] Spring Security (2) (0) | 2022.10.05 |
[Security] Spring Security (1) (0) | 2022.10.05 |
댓글