본문 바로가기
Development/Spring

Spring Security 기본 개념과 동작구조 이해

by 메정 2021. 8. 24.

현재 프로젝트를 병행하면서 Spring 프레임 워크에 대해서 공부하고 있는데
OAuth 2.0을 이용한 소셜로그인 구현을 위해선 Spring Security에 대한 선행 이해가 필요하다고 생각되어 먼저 살펴보려고 한다.

Spring Security란?

Spring 기반의 애플리케이션의 보안(인증, 권한, 인가 등)을 담당하는 스프링 하위 프레임워크

'인증'과 '권한'에 대한 부분을 Filter 흐름에 따라 처리한다.
보안과 관련한 체계적인 옵션을 제공하므로 개발자 입장에서 이 프레임워크를 이용하면 하나하나 보안 관련 로직을 작성하지 않아도 된다.

Filter는 Dispatcher Servlet 으로 가기 전에 적용되므로 가장 먼저 URL 요청을 받지만, Interceptor는 Dispatcher와 Controller 사이에 위치한다.

아키텍처 및 동작 구조

*Spring boot 버전 별로 인터페이스 명이나 클래스명이 조금씩 상이한 것 같음

Spring Security는 각각의 역할에 맞는 작업을 처리하는 여러 개의 필터들이 체인형태로 구성되어 순서에 따라 순차적으로 수행

1.사용자가 아이디 비밀번호로 로그인을 요청함
2.AuthenticationFilter에서 UsernamePasswordAuthenticationToken을 생성하여 AuthenticaionManager에게 전달
3.AuthenticaionManager는 등록된 AuthenticaionProvider(들)을 조회하여 인증을 요구함
4.AuthenticaionProvider는 UserDetailsService를 통해 입력받은 아이디에 대한 사용자 정보를 DB에서 조회함
5.입력받은 비밀번호를 암호화하여 DB의 비밀번호화 매칭되는 경우 인증이 성공된 UsernameAuthenticationToken을 생성하여 AuthenticaionManager로 반환함
6.AuthenticaionManager는 UsernameAuthenticaionToken을 AuthenticaionFilter로 전달함
7.AuthenticationFilter는 전달받은 UsernameAuthenticationToken을 LoginSuccessHandler로 전송하고, SecurityContextHolder에 저장함

인증과 인가

1.인증(Authentication) : 해당 사용자가 본인이 맞는지를 확인하는 절차
2.인증(Authorization): 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차

Spring Security는 기본적으로 인증 절차를 거친 후 인가 절차를 진행
인가 과정에서 해당 리소스에 대한 접근 권한이 있는지 확인
인증과 인가를 위해 Principal을 아이디로, Credential을 비밀번호로 사용하는 **Credential 기반의 인증 방식 사용**

Principal(접근 주체) : 보호받는 Resource에 접근하는 대상
Credential(비밀번호) : Resource에 접근하는 대상의 비밀번호

Spring Security 주요 모듈

SecurityContextHolder

  • 보안 주체의 세부 정보를 포함하여 응용 프로그램의 현재 보안 컨텍스트에 대한 세부 정보를 저장
  • 기본적으로 SecurityContextHolder.MODE_INHERITABLETHRADLOCAL 방법과 SecurityContextHolder.MODE_THREADLOCAL 방법 제공

Spring boot 2.3.1 기준 3가지 공유 전략 제공

  1. SecurityContextHolder.MODE_INHERITABLETHRADLOCAL : ThreadLocalSecurityContextHolderStrategy 클래스를 구현체로 사용하며, ThreadLocal(같은 쓰레드 내에서 공유 가능한 자원)을 사용하여 SecurityContext 를 공유. (default 설정)
  2. SecurityContextHolder.MODE_THREADLOCAL : InheritableThreadLocalSecurityContextHolderStrategy 클래스를 구현체로 사용하며, InheritableThreadLocal(자식 쓰레드까지 공유 가능한 자원)을 사용하여 SecurityContext 를 공유
  3. SecurityContextHolder.MODE_GLOBAL : GlobalSecurityContextHolderStrategy 클래스를 구현체로 사용하며, static 선언하여 SecurityContext 를 저장. 따라서, 해당 JVM 내의 인스턴스들은 모두 공유

공유 전략 설정하는 방법

SecurityContext

  • Authentication을 보관하는 역할
  • SecurityContext를 통해 Authentication 객체를 꺼내올 수 있음

Authentication

  • 현재 접근하는 주체의 정보와 권한을 담는 인터페이스
  • Authentication 객체는 SecurityContext에 저장
    // SecurityContextHolder를 통해 SecurityContext에 접근 -> SecurityContext를 통해 Authentication 객체에 접근


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  

형태로 Principal를 가져옴. 즉, SecurityContextHolder에 Security Context가 담기고, 이 Context를 통해 인증정보를 가져올 수 있음

UsernamePasswordAuthenticationToke`

  • Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스
  • User의 ID가 Principal 역할을 하고, Password가 Credential의 역할을 수행
  • UsernamePasswordAuthenticationToken의 첫 번째 생성자는 객체를 생성, 두번째 생성자는 인증이 완료된 객체를 생성

AuthenticationProvider

  • 실제 인증에 대한 부분 처리
  • 인증 전의 Authentication 객체를 받아 -> 인증이 완료된 객체를 반환
  • AuthenticationProvider 인터페이스를 구현하여 Custom해 AuthenticationManager에 등록

AuthenticationManager

  • SpringSecurity의 인증 부분을 처리
  • AuthenticationManager에 등록된 AuthenticationProvider에 의해 처리
    • AuthenticationProvider를 List로 갖으며, AuthenticationManager는 for문을 통해 모든 provider를 조회하면서 authenticate 처리
  • 인증이 성공하면 두번째 생성자를 이용 -> 인증이 성공한 객체(isAuthenticated-true)를 생성하여 Security Context에 저장
  • 인증 상태 유지를 위해 세션에 보관, 인증이 실패한 경우 AuthenticationException 발생

--- 아래부터는 아이디를 기반으로 DB에서 데이터 조회, 암호화 방식, 토큰 전달 ---

UserDetails

  • 인증에 성공하여 생성된 UserDetails 객체는 Authentication 객체를 구현한 UsernamePasswordAuthenticationToken를 생성하기 위해 사용하는 인터페이스
  • 정보를 반환하는 메소드를 갖고 있음(getter)

UserDetailsService

  • UserRepository를 주입받아 DB와 연결 처리

Password Encoding

  • AuthenticationManagerBuilder.userDetailsService().passwordEncoder()를 통해 패스워드 암호화에 사용될 PasswordEncoder 구현체 지정
  • 실무에서는 bcrypt 암호화 방식을 주로 사용

bcrypt 암호화 방식 : Niels Provos 와 David Mazières가 설계한 암호 해싱 함수 (단방향 해싱 함수)

문자열 형태  
$2b$\[cost\]$\[22 character salt\]\[31 character hash\]  
ex) $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy


$10: Cost factor (2 ==> 1,024 rounds) (연산량, 소요시간) *cost의 값이 클수록 연산의 cost가 증가 ex. cost = 10 → 2^10번 돌린 것  
N9qo8uLOickgx2ZMRZoMye: 16바이트(128비트) 솔트, base64-encoded to 22 characters *cost의 정보를 통해 생성된 salt  
IjZAgcfl7p92ldGxad68LJZdL17lhWy:  24바이트(192비트) 해시, base64-encoded to 31 characters *hash 값 (암호문)

GrantedAuthority

  • GrantedAuthority : 현재 사용자(Principal)가 가지고 있는 권한
  • ROLE_ADMIN, ROLE_USER와 같이 ROLE_* 의 형태로 사용
  • 보통 roles 라고 함
  • GrantedAuthorityrorcpsms Service에 의해 불러올 수 있고, 특정 자원에 대한 권한 여부를 검사하여 접근 허용 여부 결정

참고자료

망나니 개발자님 블로그 //다음 게시글에는 소스코드도 친절하게 있음!
재미있는 개발공부 // SecurityContextHolder 공유 전략 설정 방법 명시되어 있음!
김찬정님 개발 블로그 //Spring Security 기본 개념과 동작구조를 이해할 수 있음!

 

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

Java Optional 바르게 쓰기  (0) 2021.09.23
Unit Test에서 AssertThat을 사용  (0) 2021.09.23
테스트 코드 작성 시 유의사항  (0) 2021.09.23
@Validation 어노테이션  (0) 2021.09.23
빠르게 실패 vs 안전하게 실패  (0) 2021.09.23

댓글