이전 글 : https://velog.io/@tofha054/Spring-Spring-Security-1도-몰랐던-사람

이해하기 정말좋은 사진이있어서 가져와봤다.
여기서 서블릿 컨테이너와 스프링 컨테이너로 나뉘는데,
이것저것 알아보다가 튜터님이 너무 깊게 알 필요가 없다고 해서 요약 팍팍해서 TIL 쓴다.
SpringSecurity 에서는 필터를 만들어낸다
보면 알겠지만 SpringSecurity은 스프링 컨테이너에 존재한다.
물론 Bean으로 등록되어있음!!!!
그럼 서블릿 컨테이너에 있는 필터체인에서는 어떻게 SpringSecurity가 만들어낸 필터로 접근하지?
이 역할을 DelegatingFilterProxy가 해준다.

필터부분만 떼서보면 이런형식이다.
서블릿 컨테이너에서는 기존에 있던 필터를 진행하다가
DelegatingFilterProxy를 만나면 필터의 흐름이 변하게된다.
그럼 해당 DelegatingFilterProxy 차례에서 SecurityFilterChain을 호출하게 되고, SecurityFilter 끝까지 도달하면
SecurityFilterChain 전체에 대해서 값이 true/false가 나오게되는데 여기서 예외를 반환하거나 다음 필터(Filter2)으로 진행한다.
DelegatingFilterProxy에는 FilterCainProxy라는 놈이있다.!
DelegatingFilterProxy : 아지금 스프링 시큐리티에서 필터만들어서 연결좀할게!
FilterCainProxy : 그 필터들은 여기있어!
요런 느낌?
그럼 스프링 시큐리티에서 우리는 뭘해야하느냐?

세션에서 본 그림이다.
해당 요소들은 모두 Filter처럼 생각할 수 있고, 이를 설정하는 클래스가 SecurityConfig 클래스이다.
우리가 자주 사용하는 JWT 토큰에 대해 인가 작업을 실행하는 BearerTokenProvider와 구글,네이버,카카오 등의 OAuth 를 사용할 때 인가에 필요한 작업을 하는 OAuthProvider 등도 여기서 볼 수 있음.
SecurityFilterChain에서는 최종적인 목표가 SecurityContextHolder의 내용을 채우는 것이다.
아래에 곧 나올 그림은 SecurityFilterChain의 모든 필터들인데 Spring Security가 기본적으로 모두 제공하고있다.
여기서 의문
우리가 프로젝트에서 사용하던 jwtFilter에서는 바로 SecurityContextHolder를 채우는뎅??

jwtFilter에서 많이 써봤을 사람도있겠는데...
우리가 정의하는 필터에서 바로 ContextHolder내용을 채우고있다.
그리고 이 내용이 있다면 SecurityConfig에도 아래와 같은 코드가 있을 것이다.

이는 ContextHolder내용을 채우는 jwtFilter를 SecurityFilterChain에 있는 필터들 순서 중에 UsernamePasswordAuthenticationFilter라는 클래스 앞에다가 위치시키는 것이다.

이 순서로 진행된다.
오른쪽의 구현체는 우리가 알필요가 읍슴 필요하면 찾아서 꺼내쓰는 형식이기땜시
이처럼 앞 필터에서 SecurityContextHolder가 충족되면 다음 필터들은 모두 패스된다
패스된 필터들은 어디로가는가???

서블릿 컨테이너로 돌아와서 다음 필터를 진행한다.
혹은 오류 반환을 한다.

위에서 봤던 사진을 확대해봤는데,
Authentication에는 Principle이라는 객체가 존재한다. (우리가 쓰는 @AuthenticationPrinciple 이 이 Principle이다.)

SecurityContext
└── Authentication
├── authorities (권한들)
├── details (IP, 세션 등)
├── credentials (비밀번호 or 토큰)
└── principal ← 로그인한 사람 정보 (중요)
일케생김
이처럼, Principal은 “로그인한 사용자 자신”을 나타내는 객체다.
Spring Security가 로그인 성공 시, 이 Principal 객체를 Authentication 안에 넣어주는데 우리는 jwtFilter에서 바로 집어넣고잇음..
Authentication auth = new UsernamePasswordAuthenticationToken(
authUser,
null,
List.of(new SimpleGrantedAuthority(authUser.getUserRole().name())) // 역할 기반 권한 리스트
);
이런식으로 ㅇ_ㅇ
여기서는 커스텀한 로그인 객체를 넣어주지만, 아주 간단한 아이디/비밀번호/역할권한정보 에 관한 내용만 저장할 것이라면
UserDetails 인터페이스를 활용할 수 있음.
| 메서드 | 설명 |
|---|---|
getUsername() | 아이디(이메일 등) |
getPassword() | 비밀번호 |
getAuthorities() | 역할/권한 정보 (ROLE_USER, ROLE_ADMIN 등) |
isAccountNonExpired() | 계정 만료 여부 |
isAccountNonLocked() | 잠긴 계정인지 |
isEnabled() | 활성 계정인지 |
이로써 스프링 시큐리티 로 큰 흐름그리기 작동 순서정도 를 하루종일 배웠따ㅣ..
여기서는 커스텀한 로그인 객체를 넣어주지만, 아주 간단한 아이디/비밀번호/역할권한정보 에 관한 내용만 저장할 것이라면 UserDetails 인터페이스를 활용할 수 있음.
이 부분에서 꼭 UserDetails 를 사용해야하는 이유가 있는걸까요?
principal 자체는 Object 타입이라 원하는 필드들로 구성된 클래스를 직접 정의해 사용해도 괜찮을것 같은데, 어떤 의도인지 궁금해 코멘트 남겨봅니다! (UserDetails 인터페이스에는 요구사항에 필요하지 않은 추상 메소드들이 많아 강제되는것 같아보이는것 같습니다)
시큐리티 항상 느끼지만 너무 복잡하고 어려워요. 그래도 덕분에 조금이나마 이해 하고 가요