스프링 시큐리티란?
인증, 인가 그리고 보편적인 공격들을 방어해주는 웹 보안 관련 스프링 하위 프레임워크이다.
작동 방식

스프링 시큐리티는 Servlet Filter기반으로 작동한다.
그리고 SecurityFilterChain 라는 이름의 스프링 빈으로 Security 설정을 관리한다.
그러나 Spring Bean은 스프링 컨테이너(Application Context)에서 관리되는데
Servlet Filter는 서블릿 컨테이너(톰캣)에서 관리하므로 서로의 생성주기가 다르다.
즉 스프링 빈으로 설정한 내용을 Servlet Filter에서 사용할 수 없다.
그래서 스프링 시큐리티는
서블릿 필터에서 서블릿 필터를 구현한 스프링 빈에게 요청을 위임하는 DelegatingFilterProxy를 사용해서 실제로 동작한다.
스프링 시큐리티는 SecurityFilterChain의 설정에 맞춰 필터들을 구현한 후
서블릿 필터체인 진행 중 FIlterChainProxy(DelegatingFilterProxy)가 호출되면
SecurityFilterChain이 만들어낸 스프링 시큐리티의 필터 체인을 호출해 보안 로직을 수행한다.
여러개의 SecurityFilterChain을 만들어 보안 설정을 여러개 만들고 동시에 사용할 수 있으며
과거(스프링부트 3.0.0 이전)에는 WebSecurityConfigurerAdapter를 상속하는 Config class를 만들어 설정했지만
최근에는 직접 SecurityFilterChain을 빈으로 등록하며 설정한다.
- 사용자 요청
- ServletFilter 차례대로 동작
- DelegatingFilterProxy 동작
- FilterChainProxy 동작(요청에 맞는 SecurityFilterChain 선택)
- SecurityFilterChain에서 자신이 가진 각각의 필터를 차례대로 수행하며 보안 처리 수행
- 다음 로직 수행.
정확하게 어떻게 구현되었는지 코드 레벨로 뜯어보는 것은 너무 깊게 들어가는 것 같고,
- Spring Security는 서블릿 필터 기반으로 동작한다.
- 설정 파일에 SecurityFilterChain이라는 빈을 등록해 설정한다. (여러개 설정 가능)
- WebSecurityConfigureAdapter를 이용해 SecurityFilterChain을 생성한다. (과거의 설정 방식)
정도만 알고 넘어가면 될 것 같다.
SecurityFilterChain가 만들어내는 Security 필터들
SecurityFilterChain은 사용자의 설정에 따라 Security 필터들을 만들어낸다.
SecurityContextPersistenceFilter | SecurityContext가 있는지 확인 (인증 전 요청인지 인증 후 요청인지 확인)하는 필터 |
LogoutFilter | 로그아웃 요청인지 확인한 후 로그아웃 요청을 처리하는 필터 |
UsernamePasswordAuthenticationFilter | 전달받은 Username, password를 이용해 인증 객체를 만들고 인증을 진행하는 필터 |
ConcurrentSessionFilter | 현재 사용자의 세션 만료 여부를 체크하는 필터 |
RememberMeAuthenticationFilter | 세션이 만료되거나 무효화 되었을 시에 동작하는 필터 |
AnonymousAuthenticationFilter | 인증 시도나 권한 없이 특정 자원에 접근 시도시에 동작하는 필터 |
SessionManagementFilter | 세션정보의 등록, 조회, 삭제 등을 관리하는 필터 |
ExceptionTranslationFilter | 필터체인 동작중 발생하는 예외를 처리하는 필터 |
FilterSecurityInterceptor | 인증이 완료된 유저의 인가 로직을 처리하는 필터 |
Username/password를 사용해 로그인 하고, session을 사용해 로그인을 유지하는 방식으로 설정한다면
다음과 같은 필터들이 만들어진다.
이외에도 수많은 필터들이 존재하며 인증/인가 전략을 어떻게 세우느냐,
SecurityFilterChain을 어떻게 설정하느냐에 따라 만들어지는 필터들이 달라진다.
SpringSecurity의 핵심 요소 - Authentication, SecurityContext
1. Authenticaion이란?
스프링 시큐리티에서 사용하는 인증 인터페이스
인증 방법(username/password, oauth 등등) 에 따라 실제로 구현되고 사용되는 객체가 다르지만
이번 글에선 편의상 모두 Authentication 객체라고 부르겠다.
인증 전, 인증 후에 모두 사용된다.
인증 전 : 인증 요청시에 들어온 사용자의 정보를 담아 인증 검증을 위해 사용된다.
인증 후 : 최종 인증 결과를 담아 SecurityContext에 저장되어 전역적으로 사용 가능해진다.
- principal: 사용자 아이디 (인증 전) / UserDetails 객체를 저장 (인증 후)
- credentials: 사용자 비밀번호 저장 (인증 전) / 값 없음 (인증 후)
- authorities: 값 없음 (인증 전) / 인증된 사용자의 권한 목록 저장 (인증 후)
- details: 인증 부가 정보
- Authenticated: false (인증 전) / true (인증 후)
이러한 정보들을 담고 있는데 뒤의 인증 로직에서 어떤 값들이 들어가 어떻게 사용되는지
예시와 함께 다시 보면 이해가 더 쉬울 것 같다.
2. SecurityContext란?
Authentication 객체를 담고 있는 저장소로 필요할때 Authentcation 객체를 꺼내 쓸 수 있도록 제공되는 클래스이다.
ex) 사용자 인증 완료 후, 사용자 정보를 화면에 보여주고 싶음 -> 컨트롤러에서 인증한 사용자의 정보가 필요함
기본적으론 ThreadLocal에 저장되어 다른 Thread로부터 안전하며,
로직 아무곳에서나 참조가 가능하도록 설계되었다.
또 인증이 완료되고 응답을 반환했다면, HttpSession에 저장되어,
동일한 유저에게 다시 요청이 왔을때 재사용할 수 있다.
SecurityContextHolder는 SecurityContext 객체를 감싸는 wrapper 클래스이며,
SecurityContext를 어떻게 저장하는지 설정할 수 있다. (스레드 당 할당, 자식 스레드와 공유, 전체 공유 등등)
AuthenticationFilter - 인증 로직

AuthenticationFIlter은 인증을 담당하는 필터 인터페이스이다.
SecurityFilterChain 설정에 따라 구현되는 필터가 달라지는데
username/password를 사용해 로그인 하는 설정의 경우엔
UsernamePasswordAuthenticationFilter로 구현된다.
1. AuthenticationFilter는 사용자 요청으로부터 인증에 필요한 정보를 가지고 Authentication 객체를 만들어 AuthenticationManager에게 전달한다.
UsernamePasswordAuthenticationFilter는 사용자 요청에서
- pricipal : username(로그인 아이디, 이메일 등)
- credentials : 비밀번호
다음과 같은 값을 넣어 Authentication의 객체를 생성한 후 AuthenticationManager에게 전달한다.
2. AuthenticationManager는 받은 Authentication 객체를 사용해 인증을 진행한다.
AuthenticationManager 역시 인터페이스로 주로 ProviderManager로 구현한다.
ProviderManager는 Authentication 인증 객체를 받은 후 여러개의 AuthenticationProvider 중에서
해당 인증객체를 사용해 인증을 진행할 수 있는 AuthenticationProvider를 찾아 인증 처리를 위임한다.
(username/password 인증 방식의 경우 DaoAuthenticationProvider에 넘긴다)
2-1. DaoAuthenticationProvider의 인증 처리 로직
DaoAuthenticationProvider는 UserDetailsService 객체와 PasswordEncoder 객체를 사용해 인증을 진행한다.
AuthenticationManager가 받아온 Authentication 객체엔 username(사용자 아이디, 이메일)과 password가 저장되어 있다.
- UserDetails 객체에선 Authenticaion 객체에 저장된 username의 값을 이용해 유저가 존재하는지 여부를 판단한다.
- 유저가 존재하지 않을 경우 UsernameNotFoundException을 발생시키고, 유저가 존재할 시 UserDetails 객체에 유저 정보(비밀번호, 사용자 권한 목록 등)을 담아 반환한다.
- PasswordEncoder를 사용해 Authentcaion 객체에 저장된 비밀번호와 UserDetails 객체의 비밀번호를 비교한다.
- 일치할 경우 인증이 성공했으므로 Authentcaion에 성공한 유저 정보 (UserDetails) 객체를 담아 반환한다.
3. AuthenticationManager는 인증 성공시 Authentication 객체를 SpringContext에 저장한다
최종적으로 만들어진 Authentication 객체는 SecurityContextHolder에 저장되어 필터가 종료된 이후에도 컨트롤러 로직 등에서 전역적으로 사용할 수 있도록 한다. (인증에 성공한 유저 정보를 컨트롤러에서 사용하도록)
또한 컨트롤러 로직이 모두 종료되고 요청 처리가 끝난 후 최종적으로 응답하기 전에
SecurityContextHolder의 SecurityContext 정보를 httpSession에 저장해놓은 후 응답하고,
SecurityContextHolder는 비워버린다.
FilterSecurityIntercepter - 인가 로직
1. SecurityContextPersistenceFilter에서 인증 전인지, 인증 후인지 판단한다.
세션을 이용하도록 SecurityFilterChain을 설정했다면
httpSession을 사용해 SecurityContext가 저장되어있는지 유무를 확인한다.
SecurityContext가 httpSession에 저장되어 있다면
세션에서 SecurityContext를 꺼내 SecurityContextHolder에 집어넣는다.
2. FilterSecurityIntercepter에서 인증 여부를 확인한다.
FilterSecurityIntercepter는 SecurityContext가 인증 객체(Authentication)를 가지고 있는지 확인한다.
3. 인증된 요청일 경우 사용자가 요청한 자원에 필요한 권한 정보를 조회한다.
인증 객체가 있는 경우 SecurityMetadataSource가 사용자가 요청한 정보의 권한 정보를 전달해준다.
요청한 정보에 접근 권한 설정이 없는 경우 바로 접근 허용한다.
4. AccessDecisionManager에게 권한 정보와, 사용자 정보를 보내 접근 승인 판단을 위임한다.
AccessDecisionManager에게 권한 정보 위임 후 접근 승인 판단을 위임한다.
AccessDecisionManager는 받은 권한 정보와 사용자 권한 정보를 이용해 사용자의 자원 접근을 허용/거부한다.
AccessDecisionManager는 AccessDecisionVoter 객체들을 사용해 승인, 거부 여부를 받은 후 최종적인 접근 결정을 내린다.
https://catsbi.oopy.io/f9b0d83c-4775-47da-9c81-2261851fe0d0
스프링 시큐리티 주요 아키텍처 이해
목차
catsbi.oopy.io
다음 글에선 세션과 username/password 방식을 이용해서 로그인 했던 기존 프로젝트에
spring security를 적용하는 방법에 대해 써보겠다.
'Spring > Spring Security' 카테고리의 다른 글
사지방에서 Spring Security 공부하기 #6 - OAuth2 + JWT를 이용한 인증, 인가 구현 (1) | 2024.01.02 |
---|---|
사지방에서 Spring Security 공부하기 #5 - OAuth2 + Session을 이용한 인증, 인가 구현 (4) | 2023.12.29 |
사지방에서 Spring 공부하기 Spring Security #4 - JWT를 이용한 인증 인가 적용 코드 (0) | 2023.10.19 |
사지방에서 Spring 공부하기 Spring Security #3 - JWT를 이용한 인증 인가 적용하기 (3) | 2023.10.15 |
사지방에서 Spring 공부하기 - 기존 프로젝트에 Spring Security 적용기 (0) | 2023.09.27 |