728x90

Spring Security 예제

우선 코드는 깃허브에 있다.

일단 문서로 정리한 것을 토대로 로그인과 회원가입 어떤 순서로 동작하는지 알아보려고 했다.

로그인

일단 시큐리티에 대한 설정은 SecurityConfig 라는 클래스 설정파일을 만들어서 진행했다.

WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapterWebSecurityConfigurer 라는 인터페이스를

조금 더 쉽게 생성하기 위해 존재하는 클래스이다. 이 클래스의 구현을

그러니까 기본으로 적용되어 있는것 외에 재정의 하여 사용할 수 있다.

아래는 해당 추상클래스에 대한 설명을 가져와봤다.

스크린샷 2021-12-05 오후 8 51 54

우리는 이 추상 클래스에서 구현되어있는 configure(HttpSecurity http) 메소드를 재정의 하여 설정을 진행한다.

스크린샷 2021-12-05 오후 8 55 19

SecurityConfig

package io.github.lsj8367.security.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/", "/h2-console/**"); // "/" 와 "/h2-console"은 접근 가능
        //여기서 무시하는 과정이 아래의 configure()보다 우선순위가 높다.
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated() //모든 요청은 인증이 필요함 위의 ignoring()에 포함된 애들은 제외시킨다. 추가적으로 permitAll()로 해제해준 url은 접근 가능
            .and()
            .formLogin() //formLogin 설정 default /login
            .and()
            .logout().permitAll(); //logout에 대한 모든권한 적용 default /logout
    }

}

여기 설정에서 formLogin()은 uri를 잡아주지 않았기에 기본값인 /login으로 로그인을 처리한다.

UsernamePasswordAuthenticationFilter

이때 동작하는 필터가 UsernamePasswordAuthenticationFilter인데 이 클래스는

AbstractAuthenticationProcessingFilter를 상속받아 처리해준다.

UsernamePasswordAuthenticationFilter 클래스는 기본값인 /login에 대해 응답한다고

설명에 명시되어 있다. 여기서 사용되는 username, passwordconfigure()에서

usernameParameter()passwordParameter()로 변경해서 매칭해줄 수 있다.

인증에 대한 검증을 수행하게 되는데

스크린샷 2021-12-05 오후 9 07 52

HTTP 메소드가 POST인지를 먼저 판단한다.

그 후에 obtainUsername, obtainPassword로 파라미터 설정한것을 가져온다.

나는 기본값으로 두었기에 form안에 input id가 username, password 이다.

그다음에 null인지, 빈문자열인지 검증을 한 뒤에

UsernamePasswordAuthenticationToken을 발급해준다.

이렇게 로그인 인증이 일단락 되었다.

728x90

'Spring > Security' 카테고리의 다른 글

Authentication 인증  (0) 2022.08.09
개요  (0) 2022.08.09
728x90

📌 Spring Security

Spring SecurityServlet Filters를 기반으로 동작한다.

Spring Security는 인증, 권한 부여 및 보호를 제공하는 프레임워크이다.

스크린샷 2021-12-03 오후 10 38 42

전체적인 구조는 위와 같다.

Gradle 설정

plugins {
      id 'io.spring.dependency-management' version "1.0.10.RELEASE"
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
}

📌 Filter

그래서 필터의 역할을 먼저 아는것이 중요한데,

Filter 영어 단어만 봐도 뭔가를 필터로 걸러주는 느낌이 난다.

업무중에 통신 하나하나의 로그를 다 찍는 과정을 Filter를 통해서 구현했던 경험이 있다.

이 필터를 줄줄이 연결하면 FilterChain이 되는 것이다.

다음은 스프링 공식문서에서 FilterChain의 구조를 가져왔다.

구조

스크린샷 2021-12-01 오후 9 19 39

클라이언트는 서버에 요청을 보내고 컨테이너는 요청 URI를 기반으로 HttpServletRequest를 처리하는 필터와

필터체인을 생성한다. Spring MVC에서의 ServletDispatcherServlet이 된다.

Filter 인터페이스를 구현하면 doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

메소드를 구현해주어야 한다.

chain.doFilter()를 호출하면 다음 필터를 차례대로 수행하는 것이다.

이렇게 필터들이 쭉 연결되어 기능을 수행한다고 생각하면 된다.

📌 DelegatingFilterProxy

구조

스크린샷 2021-12-01 오후 9 30 41

설명

SpringServlet 컨테이너의 라이프사이클과

SpringApplicationContext 사이의 연결을 허용하는

DelegatingFilterProxy라는 필터를 제공한다.

이 필터는 Servlet Filter로 애플리케이션 컨텍스트에서 요청한 것을 스프링 컨테이너에 생성된

Bean Filter를 찾고 그 필터를 호출한다.

DelegatingFilterProxy는 컨테이너가 시작되기 전에 필터를 등록해야 하기 때문에 중요하다.

📌 FilterChainProxy

Spring Security의 서블릿 지원은 FilterChainProxy에 포함되어 있다.

FilterChainProxySecurityFilterChain을 통해 여러 필터 인스턴스에 위임할 수 있도록

시큐리티에서 제공하는 특수 필터이다.

FilterChainProxyBean이라서 DelegatingFilterProxy안에 포함된다.

구조

스크린샷 2021-12-01 오후 9 45 51

이제보니 구조가 좀 잡혀가는것 같다.

클린코드와 자바의 정석을 읽었더니 시큐리티 아키텍처가 이해되는건 무엇일까.... 🤔🤔🤔

📌 SecurityFilterChain

SecurityFilterChain은 클라이언트에서 필터를 돌다가 DelegatingFilterProxy에 들어온 요청을

받아서 이 요청에 대해 처리해야 하는 Security Filter를 찾아 수행한다.

구조

스크린샷 2021-12-01 오후 9 57 39

📌 Security Filter

핵심인 시큐리티 필터이다. 이 필터는 SecurityFilterChain API를 사용해

FilterChainProxy에 삽입된다. 이 필터들은 순서가 중요하다.

필터의 순서를 알 필요는 없다.

근데 알아두면 유용하게 사용할 수는 있다.

필터에는 다음과 같은 필터들이 존재하고 위에서 아래로 순서가 매겨져 있다.

  • ChannelProcessingFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter

블럭이 쳐져있는 필터들을 유의해서 더욱 살펴봐야 하겠다.

📌 Handling Security Exceptions

위에서 ExceptionTranslationFilter를 사용하면 AccessDeniedException이나,

AuthenticationException을 HTTP Response로 변환할 수 있다.

스크린샷 2021-12-01 오후 11 16 45
  1. 일단 들어온 요청에서 FilterChain.doFilter()로 나머지 애플리케이션을 호출한다.
  2. 사용자가 인증이 되지 않았거나, AuthenticationException이 발생한 경우에 인증을 진행한다.

    이 경우 사용자가 성공적으로 인증되면 RequestCache에 사용자를 저장하고 이 캐시를 사용하여 원래 요청을 쭉 진행한다.
  3. 여기서 AccessDeniedException이 발생하게 되면 액세스는 거부되고, AccessDeniedHandler는 이 예외를 처리하기 위해 호출된다.

이 설정이 AccessDeniedExceptionAuthenticationException에 대한 예외처리가 없다면

ExceptionTranslationFilter는 아무것도 수행하지 않는다.

정리

여기까지가 일단 스프링 시큐리티의 기본적인 흐름이다.

다음 포스팅에서는 인증(Authentication) 에 대해서만 쭉 다뤄보는것으로 하겠다.

어렴풋이 알고 있는 기본값들에 대해서만 사용할게 아니라 입맛에 따라 커스텀하며 필터를 작업해주는 것이

개발자의 역량이라고 생각한다. 이게 뒷받침되려면 당연히 이런 공식문서를 읽어보는게 답이다.

전체적인 아키텍처의 흐름을 알아야 잘 설계할 수 있다.

아무튼 이 아키텍처를 공부함으로써 어제의 나보다는 오늘이 더 성장했다.

업무나 프로젝트에 들어간 시큐리티에 대한 코드를 보면 지금보다 더 이해할거라고 자신한다 🔥🔥🔥🔥

728x90

'Spring > Security' 카테고리의 다른 글

로그인 동작 순서  (0) 2022.08.09
Authentication 인증  (0) 2022.08.09

+ Recent posts