Spring Security란?
스프링에서 제공하는 공식 문서 ➡️ https://spring.io/projects/spring-security
스프링 시큐리티란 ?
스프링 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크
🔐 보안 언어 정리
이름설명인증 (Authenticate) | 접근하려는 유저가 누구인지 확인하는 절차 EX) 회원가입하고 로그인 |
인가 (Authorization) | 인증된 사용자에 대해서 권한을 확인하고 허락하는 것 |
접근 주체 (Principal) | 보호된 대상에 접근하는 유저 |
비밀번호 (Credential) | 대상에 접근하는 유저의 비밀번호 |
인증과 인가에 대해서 예시를 들어보자면
한 사용자가 velog 에 글을 작성하기 위해서 로그인을 했다 ➡️ 인증
로그인 한 사용자가 신나게 글을 썼다. 그리고 다른 사람 글을 수정하기 위해서 권한을 확인해봤지만 수정할 수 없었다 ➡️ 인가
그니깐 간단하게 생각해서 사이트에 대해서 유효한 사용자인지 확인하는 것이 인증 이고, 인증된 사용자가 사용할 수 있는 기능인지 확인하는게 인가 라고 생각하면 된다 그렇기 때문에 인증이 먼저 이루어 지고 인가가 이뤄져야 한다.
Spring Security 에서는 이러한 인증, 인가를 위해 Principal 을 아이디로 Credential 을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용한다.
서블릿 필터
출처 : https://limdevbasic.tistory.com/19
스프링 시큐리티는 서블릿의 필터를 기반으로 동작한다.
사용자의 요청이 서블릿에 전달되기 전, 스프링 시큐리티는 필터의 생명주기를 이용해서 인증과 권한 작업을 수행 하지만, 서블릿 컨테이너는 스프링 컨테이너에 등록된 빈을 인식할 수 없다.
그렇기 때문에 스프링 시큐리티에서는 DelegatingFilterProxy 라는 서블릿 필터의 구현체를 제공한다. DelegatingFilterProxy 는 서블릿 매커니즘으로 서블릿의 필터로 등록될 수 있으며, 스프링에 등록된 빈을 가져와서 의존성을 주입할 수도 있다. 결론적으로 서블릿 컨테이너의 생명주기와 스프링의 ApplicationContext 사이를 연결하는 다리 역할을 하게된다.
FilterChainProxy
DelegatingFilterProxy 를 통해 받은 요청과 응답을 스프링 시큐리티 필터체인에 전달하고 작업을 위임하는 역할을 한다.
🤔 DelegatingFilterProxy 에서 바로 SecurityFilterChain 을 실행시킬 수 있지만 중간에 FilterChainProxy 를 둔 이유 ?
➡️ 서블릿을 지원하는 시작점 역할을 하기 위함이다. 이를 통해 서블릿에서 문제가 발생하는 경우 FilterChainProxy 의 문제라는 걸 알 수 있다.
또한, FilterChainProxy 에서 어떤 체인에게 작업을 위임할지도 결정할 수 있음
SecurityFilterChain
인증을 처리하는 여러 개의 시큐리티 필터를 담는 필터 체인
여러 개의 SecurityFilterChain 을 구성하여 매칭되는 URL 에 따라 다른 SecurityFilterChain 이 사용되도록 할 수 있다
SecurityFilters
요청을 스프링 시큐리티 매커니즘에 따라 처리하는 필터
SecurityFilters 에는 순서가 존재함
아키텍처
Username and Password 방식의 아키텍처는 다음과 같다.
spring security 는 기본적으로 세션-쿠키 방식으로 인증한다
- 유저가 로그인 요청 (Http Request)
- AuthenticationFilter 에서 UsernamePasswordAuthentication Token 을 생성하여 AuthenticationManager 에 전달
- AuthenticationManager 은 등록된 AuthenticationProvider 들을 조회하여 인증 요구
- AuthenticationProvider 은 UserDetailService 를 통해 입력받은 아이디에 대한 사용자 정보를 User(DB) 에서 조회
- User 에 로그인 요청한 정보가 있는 경우 UserDetails 로 꺼내서 유저 session 생성
- 인증이 성공된 UsernameAuthenticationToken 을 생성하여 AuthenticationManager 로 반환
- AuthenticationManager 은 UsernameAuthenticationToken 을 AuthenticationFilter 로 전달
- AuthenticationFilter 은 전달받은 UsernameAuthentication 을 LoginSuccessHandler 로 전송하고, spring security 인메모리 세션저장소인 SecurityContextHolder 에 저장
- 유저에게 session ID 와 응답을 내려줌
1. AuthenticationFilter
- Spring Security 는 연결된 필터를 가지고 있음
- 모든 Request 는 인증과 인가를 위해서 이 필터를 통과
- SecurityContext 에 사용자의 세션 ID 가 있는지 확인 하고 세션 ID 가 없는 경우 다음 로직 수행
- 인증 성공하는 경우 인증된 Authentication 객체를 SecurityContext 에 저장 후 AuthenticationSuccessHandler 실행
- 인증 실패하는 경우 AuthenticationFailureHandler 실행
2. UsernamePasswordAuthenticationToken
- Authentication을 구현한 AbstractAuthenticationToken의 하위 클래스
- principal ➡️ username / credentials ➡️ password
- UsernamePasswordAuthenticationToken(Object principal, Object credentials) : 인증 전의 객체를 생성
- UsernamePasswordAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) : 인증 완료된 객체를 생성
3. AuthenticationManager
- Authentication 을 만들고 인증을 처리하는 interface
- 로그인시 인자로 받은 Authentication 을 Provider 를 통해 유효한지 처리하여 Authentication 객체를 리턴한다.
ProviderManager
- AuthenticationManager 의 구현체
- 사용자 요청을 인증에 필요한 AuthenticationProvider 를 살펴보고 전달된 인증 객체를 기반으로 사용자 인증 시도
4. AuthenticationProvider
- 실제 인증을 담당하는 인터페이스
- 인증 전 Authentication 객체를 받아서 DB 에 있는 사용자 정보를 비교하고 인증된 객체를 반환
5. UserDetailsService
- DB 에서 유저 정보를 가져오는 역할
- loadUserByUsername() 메소드를 통해서 DB 에서 유저 정보를 가져온다.
- 커스텀하게 사용하고 싶다면 해당 interface 를 implements 받아서 loadUserByUsername() 메소드를 구현하면 됨
6. UserDetails
- 사용자의 정보를 담는 인터페이스
기본 오버라이드 메소드 ⬇️
메소드설명getAuthorities() | 계정의 권한 목록을 리턴 |
getPassword() | 계정의 비밀번호 리턴 |
getUsername() | 계정의 고유한 값 리턴 |
isAccountNonExpired() | 계정의 만료 여부 리턴 |
isAccountNonLocked() | 계정의 잠김 여부 리턴 |
isCredentialsNonExpired() | 비밀번호 만료 여부 리턴 |
isEnabled() | 계정의 활성화 여부 리턴 |
7. SecurityContextHolder
SecurityContextHolder
- SecurityContext 를 현재 스레드와 연결 시켜주는 역할
- 스프링 시큐리티는 같은 스레드 의 어플리케이션 내 어디서든 SecurityContextHolder 의 인증 정보를 확인 가능하도록 구현되어 있는데 이 개념을 ThreadLocal 이라고 함.
SecurityContext
- Authentication 의 정보를 가지고 있는 interface
- SecurityContextHolder.getContext() 를 통해 얻을 수 있음
Authentication
- 현재 접근하는 주체의 정보와 권한을 담는 인터페이스
- AuthenticationManager.authenticate(Authentication) 에 의해 인증된 principal 혹은 token
Principal | 사용자 정보 |
authorities | 사용자에게 부여된 권한 EX) ROLE_ADMIN |
credentials | 자격 증명 |
🙇🏻♀️ 레퍼런스
- [SpringBoot] Spring Security란?
- spring security 파헤치기 (구조, 인증과정, 설정, 핸들러 및 암호화 예제, @Secured, @AuthenticationPrincipal, taglib)
- Spring Security Architecture 내용정리
- 인증(Authentication)과 인가(Authorization)
- [SpringBoot] Spring Security 처리 과정 및 구현 예제
- Spring Security의 SecurityContextHolder 알아보기
- Spring Security + JWT를 통해 프로젝트에 인증 구현하기
- [Spring Security] 스프링 시큐리티 아키텍처