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

스프링 시큐리티 사용자 인증

스프링 시큐리티는 인증에 대한 정보들을 제공한다.

Servlet Authentication Architecture

인증에 대한 아키텍처는 아래와 같다.

  • SpringSecurityContextHolder
    • 인증된 사용자의 세부 정보를 저장하는 곳
  • SecurityContext
    • SpringSecurityContextHolder에서 가져온 현재 인증된 사용자 인증을 가지고 있다.
  • Authentication
    • 사용자가 인증을 위해 제공한 자격증명이나 현재 사용자를 제공하기 위한 AuthenticationManager의 입력이 될 수 있음
  • GrantedAuthority
    • 인증에 대한 권한 정보
  • AuthenticationManager
    • 스프링 시큐리티 필터가 인증을 수행하는 방법을 정의
  • ProviderManager
    • AuthenticationManager의 기본 구현체
  • AuthenticationProvider
    • ProviderManager에서 특정 유형의 인증을 진행하는 Provider
  • Request Credentials with AuthenticationEntryPoint
  • AbstractAuthenticationProcessingFilter
    • 인증에 사용되는 기본 필터

SecurityContextHolder

구조

일반적인 구조는 위와같이 생겼다.

설명

스프링 시큐리티가 인증된 사용자의 정보를 저장하는 곳이다.

값이 포함되어 있으면 현재 인증된 사용자로 사용된다.

Authentication

사용자가 인증을 위해 제공한 자격 증명 AuthenticationManager 입력값.

이 상태에서 isAuthenticated()false를 반환

현재 인증된 사용자를 반환 현재 인증은 SecurityContext에서 얻을 수 있다.

Authentication이 포함하고 있는 값들

  • principal
    • 사용자를 식별한다.
    • username, password로 인증할 때 UserDetails가 값들을 포함한다.
  • credentials
    • 비밀번호
    • 이 비밀번호는 유출되지 않게 사용자를 인증 후 지워진다.
  • authorities
    • GrantAuthority 권한을 포함한다.

GrantAuthority

사용자에게 부여된 상위 수준의 권한

이 객체는 Authentication.getAuthorities() 에서 얻을 수 있다.

이 권한은 인증받으려는 주체에게 부여된 권한이다.

이 권한은 흔히 말하는 사용자, 관리자 등등 해당 권한을 나타낸다.

이 역할로 특정 URI에 대한 권한이나 접근 권한을 제어할 수 있게 된다.

username, password 기반 인증을 사용할 시에 권한은 UserDetailService가 가지고 있다.

UserDetailService

이 클래스는 AuthenticationProvider에서 username, password로 인증하기 위해

해당 사용자 이름, 비밀번호, 기타 속성들을 검색하는데에 사용된다.

UserDetailService를 커스텀 Bean으로 정의할 수 있다.

AuthenticationManager

이 클래스는 스프링 시큐리티 필터가 인증을 수행하는 방법을 명시한 API이다.

반환되는 인증은 AuthenticationManager를 호출한 컨트롤러에 의해

SpringSecurityHolder에 설정된다.

스프링 시큐리티의 필터와 통합하지 않는 경우에는 SecurityContextHolder를 직접

설정 할 수 있고, AuthenticationManager를 사용할 필요가 없어진다.

AuthenticationManager의 구현은 어떤것이든 가능하지만, 기본 구현은 ProviderManager

ProviderManager

위에서 말했듯, 이 클래스는 AuthenticationManager의 구현체이다.

ProviderManagerAuthenticationProviders의 리스트에 위임한다.

AuthenticationProvider는 성공, 실패를 나타내거라 결정할 수 없고

다운스트림 AuthenticationProvider가 결정하도록 허용할 수 없다.

AuthenticationProvider중 어느 것도 인증을 할 수 없는 경우에는

인증 예외인 ProviderNotFoundException이 발생하여 인증이 실패하게 된다.

구조

설명했듯 각 provider들이 특정 유형의 인증을 수행한다.

ProviderManagerAuthenticationProvider가 인증을 수행할 수 없는 경우

상위의 AuthenticationProvider를 설정하여 구성이 가능하다.

상위 AuthenticationProviderProviderManager의 인스턴스이다.

스크린샷 2021-12-03 오후 10 44 17

AuthenticationProvider

ProviderManager에 여러 AuthenticationProvider를 주입할 수 있다.

AuthenticationProvider들은 저마다의 특정 유형 인증을 수행하게 되어있다.

예를 들어, DaoAuthenticationProvider는 이름/암호 기반의 인증을 사용하고,

JwtAuthenticationProvider는 Jwt 토큰 인증을 사용한다.

AbstractAuthenticationProcessingFilter

이 클래스는 사용자의 자격 증명을 인증하기 위한 기본 필터이다.

이 인증이 되기전에 AuthenticationEntryPoint를 사용하여 HTTP 요청을 한다.

Authentication으로 인증에 대한 정보들을 쭉 가져온다.

AuthenticationManager가 그 인증 정보들을 받아 여러 검증들을 수행한다.

성공하면 Success 기본 호출은 AuthenticationSuccessHandler, 실패하면 Failure로 가서 AuthenticationFailureHandler호출됨

728x90

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

로그인 동작 순서  (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