TIL(Today I Learned)

TIL - Spring Security

Happy._. 2024. 6. 11. 22:11

Spring Security

인증 , 권한 부여 및 일반적인 공격에 대한 보호를 제공하는 프레임워크

  • 인증(Authentication) : 사용자가 누구인지 확인하는 과정
  • 권한 부여(Authorization) : 인증된 사용자가 특정 자원에 접근할 수 있는지 결정하는 과정
  • 세션 관리(Session Management) : 사용자 세션을 관리하고, 동시 로그인을 제한할 수 있음
  • CORS(Cross-Origin Resource Sharing) : 다른 도메인의 리소스를 안전하게 요청할 수 있도록 하는 매커니즘
  • JWT(JSON Web Token) : 사용자의 인증 정보를 JSON 형식으로 표현한 토큰을 사용하여 상태를 유지하지 않는 인증방식을 제공

Spring Security 인증 프로세스

  1. 인증 필터가 요청을 가로챈다.
  2. 인증 책임이 인증 관리자에 위임된다.
  3. 인증 관리자는 인증 논리를 구현하는 인증 공급자를 이용한다.
  4. 인증 공급자는 사용자 세부 정보 서비스(UserDetailsService)로 사용자를 찾고 암호 인코더(PasswordEncoder)로 암호를 검증한다.
  5. 인증 결과가 필터에 반환된다.
  6. 인증된 엔티티에 관한 세부 정보가 보안 컨텍스트에 저장된다.

다음은 SpringBootWebSecurityConfiguration의 내부 코드 중 defaultSecurityFilterChain이다.

Spring Security를 사용하기 위해 build.gradle.kts에 Spring Security 관련 모듈을 추가하면 별도의 처리가 없어도 모든 요청에 대해 인증을 요구하고 기본 로그인 폼이 만들어져 있는 이유가 defaultSecurityFilterChain 메서드 내 다음과 같이 설정되어 있기 때문이다.

SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((requests) -> {
        ((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();
    });
    http.formLogin(Customizer.withDefaults());
    http.httpBasic(Customizer.withDefaults());
    return (SecurityFilterChain)http.build();
}

 

Spring은 기본적으로 Filter Chain을 통해 Controller 앞 단에서 다양한 역할들을 수행한다.

    예) 이미지 변환 혹은 데이터 압축, 암호화, 인증, 로깅, 캐싱 등 도메인 로직 외에 부가적으로 필요한 기능들을 수행

Spring은 우리가 넣고 싶은 기능을 추가할 수 있도록 DelegatingFilterProxy를 제공한다.

DelegatingFilterProxy는 Application Context와의 Bridge 역할을 하여, 우리가 작성한 Bean들을 Filter(ServletFilter)에서 사용할 수 있게 해준다.

Spring Security는 DelegatingFilterProxy에 FilterChainProxy를 제공하여 인증과 관련된 FilterChain을 적용할 수 있게 해준다.

이미지 출처: Architecture :: Spring Security

 

요청에 따라 서로 다른 FilterChain이 적용되게 할 수 있는 기능도 적용할 수 있다.

    예) URL별로 인증 혹은 인가에 대해 다르게 처리해야할 때

 

Spring Security의 인증 처리 과정

이미지 출처: Servlet Authentication Architecture :: Spring Security

  • Authentication: 인증 정보를 담고 있는 객체로 이를 통해 인증 여부를 확인
  • Principal: User의 식별자, User Id, Email과 같은 정보를 담고 있는 객체
  • Credentials: Password와 같은 중요한 정보로, 유출방지를 위해 인증된 이후 삭제됨
  • Authorities: 권한 정보, Role 또는 Scope로 설정

SecurityContextHolder는 SecurityContext를 관리하는 역할을 하고 SecurityContext는 Authentication 객체를 담는 Container 역할을 한다.

Authentication 인터페이스는 다음과 같고 이 인터페이스의 구현체를 작성하여 인증 정보를 담는 객체로 사용한다.

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities(); // 사용자의 권한(Role)을 반환

    Object getCredentials(); // 사용자 인증에 사용되는 자격 증명을 반환(예: 비밀번호)

    Object getDetails(); // 인증에 대한 추가 세부 정보를 반환

    Object getPrincipal(); // 인증된 주체를 반환(예: 사용자)

    boolean isAuthenticated(); // 현재 사용자가 인증되었는지 여부를 반환

    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; // 사용자의 인증 상태 설정
}

 

인증 과정의 핵심은 인증 필터를 통해 요청을 받아 인증 여부를 판단하고 인증 됐을 시 SecurityContextHolder에 Authentication 객체를 할당하는 것이 핵심이다.

 

인증을 담당하는 Filter 객체를 직접 작성하거나 Spring Security에서 제공하는 Filter들을 이용하여 Spring Security에 등록해주면 인증을 처리할 수 있다.

Spring Security는 인증 처리를 위한 다양한 요소들을 제공한다.

Filter에서 Authentication 객체를 바로 다뤄 인증을 처리하기 보다 HTTP 요청으로부터 받은 정보를 Authentication 객체에 담아준 후 실제 인증을 위한 처리는 Filter가 아닌 다른 객체들이 담당 하도록 하는데 이런 역할을 하는 대표적인 요소들은 다음과 같다.

  • AuthticationManager(interface) : Filter로부터 principal, credentials 등의 정보가 담긴 Authentication 객체를 받아 인증을 수행하는 역할을 한다.
  • ProviderManager
    • AuthenticationManager의 구현체
    • AuthenticationProvider의 support(authentication: Authentication) 함수를 통해 어떤 Provider를 사용할지 결정
  • AuthenticationProvider: Provider에 주입될 수 있는 객체로 각 AuthenticationProvider는 서로 다른 종류의 인증을 수행
    • EmailPasswordAuthenticationProvider
    • JwtAuthenticationProvider

여러 인증 방식을 지원한다면, 각 인증 과정마다 Authentication 객체에 담기는 다른 정보들을 처리하기 위해 인증 과정 별 Authentication 인터페이스의 구현체를 만든다.

각 AuthenticationProvider에서 support 함수를 작성, 어떤 Token을 지원하는 인증을 제공하는지 표기해 ProviderManager가 인증 Token별로 적절한 AuthenticationProvider를 쓸 수 있게 해줄 수 있다.

 

최종적으로 AuthenticationManager(Provider Manager)에서 authentication()를 호출하면 AuthenticationProvider의 authenticate()가 호출되고 인증이 진행된다.

Authentication 객체를 확인해 인증 정보를 확인하고 인증이 되면 Authentication 객체에서 setAuthentication(true)를 호출해주고 중요 정보인 credential을 지우는 등의 수행 후 다시 반환한다.

Authentication 객체를 확인하여 인증이 됐을 시 SecurityContextHolder에 Authentication 객체를 설정해준다.