Spring Security
Spring Security는 인증, 인가 및 일반적인 공격에 대한 보호를 제공하는 프레임워크 입니다. 서비스를 개발할때 필수적으로 구현해야하는것이 로그인, 회원가입과 권한인데요. 이를 Spring Security를 통해 쉽게 해결할 수 있습니다. 다만 이 프래임워크가 워낙 방대하고 구조를 이해하고 있지않으면 활용하기 쉽지 않습니다. 하나의 게시물로 모든 Security 기능에 대해서 소개하지 못하므로, 이 글에서는 프레임워크에 대해 기본개념과 주요기능에 대해서 소개하고 각 기능에 대한 상세한 설명은 다음 글에서 이어가도록 하겠습니다.
이 글은 Spring Boot 3.4.0, Spring Security 6.4.1 환경에서 제작되었습니다.
들어가기전에
Security를 공부하기전에 짧게 기본개념을 잡고 가겠습니다. 이해하는데 도움이 될겁니다.
1. Filter
Spring Security는 Servlet Filter를 기반으로 동작합니다.
Client - Filter - DispatcherServlet - Interceptor - Controller
2. 용어
용어 | 뜻 | 설명 |
Authentication | 인증 |
|
Authorization | 인가 |
|
Principal | 접근주체 |
|
Role | 권한 |
|
용어 간의 관계
1. Authentication -> Principal 생성
(인증 성공 시 Principal 정보가 생성됨)
2. Principal -> Role 부여
(인증된 사용자에게 역할이 부여됨)
3. Role -> Authorization 결정
(부여된 역할에 따라 권한이 결정됨)
3. SecurityContextHolder
SecurityContextHolder는 인증된 사용자의 정보를 저장하는 곳입니다.
4. Filter, Authentication, Authorization
Security에서 크게 Filter, Authentication, Authorization 으로 나눌 수 있습니다. 아키텍처를 볼 때, 이렇게 3개로 구분해서 보면 이해가 조금 더 쉬울 수 있습니다.
1. 아키텍처
시큐리티 공식문서에서 가져온 아키텍처입니다. 이 사진을 보고 이해해야할것은 아래와 같습니다.
- SecurityFilterChain 은 Filter에서 동작한다.
- Authentication을 통해 인증과정을 수행한다.
인증에 성공하면 SecurityContextHolder에 인증된 사용자의 정보를 담는다.
인증에 실패하면 SecurityContextHolder가 비워집니다.(cleared out)
2. Filter
2-1. DelegatingFilterProxy
서블릿 컨테이너와 Spring의 ApplicationContext 사이의 연결점입니다. 서블릿 컨테이너는 자체 표준을 사용하여 필터 인스턴스를 등록할 수 있지만 Bean에 대해서는 알지 못합니다. 그래서 DelegatingFilterProxy 에 Bean 등록을 위임하도록 합니다.
한줄정리 : DelegatingFilterProxy는 Servlet Container - Spring ApplicationContext를 연결하는 역할을 한다!
2-2. FilterChainProxy과 SecurityFilterChain
- FilterChainProxy는 보안필터의 시작점입니다.
- 요청 URL에 따라(RequestMatcher) 적절한 SecurityFilterChain을 선택할 수 있도록 합니다.
- Spring Security의 HttpFirewall을 적용하여 특정 유형의 공격으로부터 애플리케이션을 보호합니다.
- FilterChianProxy는 bean 이기때문에 DelegatingFilterProxy로 감싸져있습니다.
2-3. SecurityFilterChain 내부 Filter
spring-security/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java at
Spring Security. Contribute to spring-projects/spring-security development by creating an account on GitHub.
github.com
SecurityFilterChain에는 여러 Filter가 존재하는데 그 Filter의 순서는 FilterOrderRegistration.class 를 통해 확인할 수 있습니다. Filter의 순서를 외울필요는 없지만 어느 정도 흐름은 알고있는게 좋습니다.
각 Filter의 용도와 사용법은 다음 글에서 알아보도록하겠습니다.
3. Authentication (인증)
사용자 정보를 가지고 인증을 하는 과정입니다.
3-1. SecurityContextHolder
- SecurityContextHolder는 인증된 사용자의 정보를 담고있는 Container입니다. Spring Security는 SecurityContextHolder가 어떻게 채워지는지 묻지않습니다. 그저 값이 포함되어있으면 인증된 사용자로 인식합니다. 즉, SecurityContextHolder가 비어있으면 인증되지않은 사용자, 비어있지않으면 인증된 사용자로 인식됩니다.
- ThreadLocal을 사용하여 보안정보를 저장합니다. 즉, SecurityContext는 동일한 스레드내에서는 어디서든 보안 정보를 쉽게 접근 가능합니다. 요청 처리가 끝나면 반드시 쓰레드의 정보를 지워야 하지만 FilterChainProxy가 이 청소를 자동으로 해주기때문에 매우 안전하게 사용할 수 있습니다.
- ThreadLocal에 대해 안전하지않은 어플리케이션에 대해서는 ThreadLocal 설정을 변경할 수 있습니다.
[JAVA] ThreadLocal에 대해서 알아보자
개요Spring Security 에서 ThreadLocal에대해서 언급한적이 있어 글로 남겨봅니다. ThreadLocal이란?ThreadLocal은 Java에서 제공하는 클래스로, java.lang 패키지에 존재합니다.각 스레드마다 독립적인 변수를
tmd8633.tistory.com
3-2. SecurityContext
인증된 객체(Authentication)를 보관하는 역할을 합니다. SecurityContextHolder로 부터 가져올 수 있습니다.
3-3. Authentication
현재 접근하는 사용자의 정보와 권한을 담은 인터페이스 입니다. isAuthenticated() 메소드로부터 인증되었는지 확인할 수 있습니다.
용어 | 설명 |
principal | 아이디/비밀번호로 인증할때 사용자를 식별하는 역할을 합니다. UserDetails의 인스턴스로 사용됩니다. |
credentials | 비밀번호로 사요됩니다. 사용자가 인증된 후에는 비밀번호가 지워집니다. |
authorities | 사용자에게 부여된 권한들입니다. GrantedAuthority의 List형식으로 되어있습니다. |
GrantedAuthority
주체에게 부여된 권한입니다. "ROLE_ADNATIOR", "ROLE_HR_SUPERVISOR" 과 같은 '역할'로 사용됩니다.
사용자 아이디/비밀번호 기반 인증을 사용할 때, UserDetailsService에 의해 로드됩니다.
3-4. AuthenticationManager
AuthenticationManager는 Spring Security의 필터가 인증을 수행하는 방식을 정의하는 인터페이스입니다.
AuthenticationManager는 인터페이스이므로 따로 구현할 수 있지만 기본적으로 ProviderManager가 구현되어있습니다.
3-5. AuthenticationProvider
실제 인증에 대한 부분을 담당합니다. 인증전의 Authentication 객체를 받아서 인증이 완료된 Authentication 객체를 반환합니다.
3-6. UserDetailService
UserDetailService를 implements해서 DB 데이터를 주입하여 UserDetails 를 반환하게 합니다.
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
3-7. UserDetails
인증된 사용자로써 사용될 인터페이스 입니다. implements해서 Custom해야합니다.
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
default boolean isAccountNonExpired() {
return true;
}
default boolean isAccountNonLocked() {
return true;
}
default boolean isCredentialsNonExpired() {
return true;
}
default boolean isEnabled() {
return true;
}
}
4. Authorization (인가)
인증된 정보를 가지고 접근에 대한 허용여부를 결정하는 과정입니다. GrantedAuthority에 String getAuthority() 메소드가 사용됩니다.
기본적인 규칙으로 'ROLE_' 접두사가 포함됩니다. 즉, "USER"라는 권한이 있는 경우 GrantedAuthority#getAuthority 를 사용하면 "ROLE_USER" 권한을 찾습니다.
'ROLE_' 접두사를 변경할 수 있습니다.
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
// static 필수!!
5. 인증처리 과정
5-1. HTTP 요청
사용자가 로그인 요청을 합니다.
5-2 AuthenticationFilter 인증
SecurityFilterChain에서 로그인을 담당하는 UsernamePasswordAuthenticationFilter 에서 인증을 처리합니다.
5-3. UsernameAuthenticationToken 발급
아이디와 비밀번호를 통해 토큰을 발급하고 AuthenticationManager로 토큰을 보냅니다.
5-4. AuthenticationProvider 인증
인증된 객체를 UserDetailsService에게 넘겨줍니다.
5-5. UserDetails 생성
전달된 인증된 객체를 DB에서 조회하여 데이터를 UserDetails 반환
5-6. AuthenticationProvider
인증에 성공하면 AuthenticationProvider에서 AuthenticationManager에게 인증에 성공한 객체를 반환
5-7. AuthenticationManager
인증에 성공한 객체를 AuthentifacionFIlter에 전달
5-8. SecurityContextHolder
인증에 성공한 객체는 SecurityContextHolder에 저장
6. 설정
SecurityFilterChain 에 어떤 기능을 사용할지 설정하는 부분입니다. 이 부분에서는 Filter의 기능을 사용자의 API에 맞게 수정할 수 있고, 해당 Filter의 전과 후에 Custom Filter를 넣는 등 여러 기능을 설정할 수 있습니다.
6-1. 기본설정
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults())
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
);
return http.build();
}
}
메소드 | 필터 | 설명 |
csrf | CsrfFilter | csrf에 대한 설정 |
httpBasic | BasicAuthenticationFilter | HTTP Basic Authentication에 대한 설정 |
formLogin | UsernamePasswordAuthenticationFilter | form login에 대한 설정 |
authorizeHttpRequests | AuthorizationFilter | 권한에 대한 설정 |
위의 4개의 메소드뿐만아니라 수많은 filter에 대한 설정을 할 수 있습니다.
Customizer.withDefaults() 는 Security 기본설정에 따른다는 메소드입니다.
CSRF에 대해서 이해하기
[CS][Spring Security] CSRF란?
CSRF란?CSRF Cross-Site Request Forgery의 약자로 인증된 사용자의 권한을 악용하여 해당 사용자가 의도하지 않은 요청을 웹사이트에 전송하는 공격 기법입니다. 공격자는 사용자가 이미 인증된 상태를
tmd8633.tistory.com
6-2. Filter 추가
http.
// atFilter를 filter로 변경
addFilterAt(Filter filter, Class<? extends Filter> atFilter)
// beforeFilter 전에 filter를 추가
addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter)
// afterFilter 후에 filter를 추가
addFilterAfter(Filter filter, Class<? extends Filter> afterFilter)
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.addFilterAfter(new TenantFilter(), AnonymousAuthenticationFilter.class);
return http.build();
}
Filter를 Bean으로 등록
Filter를 Bean으로 선언하면 Spring Container에 등록되면서 Filter가 2번 호출 될 수 있습니다. 이를 방지하기 위해 setEnabled(false) 로 설정해주어야 합니다.
@Bean
public FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {
FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
이렇게 하면 HttpSecurity 에 추가한 것만 유효하게 됩니다.
참고자료
공식문서 : https://docs.spring.io/spring-security/reference/servlet/architecture.html
'FrameWork > Spring' 카테고리의 다른 글
[Spring Security] 스프링 시큐리티 Anonymous과 ExceptionHandling (4) (0) | 2024.12.15 |
---|---|
[Spring Security] 스프링 시큐리티 RememberMe (3) (0) | 2024.12.13 |
[Spring Security] 스프링 시큐리티 시작하기 (로그인, 로그아웃과 권한) (2) (0) | 2024.12.12 |
[Spring Boot] @Scheduled 스케줄 적용 (0) | 2024.11.11 |
[Spring] JAVA로 금지어 검사기를 만들어보자 (0) | 2024.09.16 |