일단 위에 블로그를 읽어보면 OncePerRequestFilter 랑 AbstractAuthenticationProcessingFilter 에 대해 나왔는데 밑이 부분 글을 읽고 무슨 말이고, 이해 하고 나서 그럼 둘중 머 사용해야하나 생각이 들었다
" OncePerRequestFilter 를 통해 JWT 인증을 구현했으면 Spring Security 를 의존하지 말아야 하는데 정작 그건 또 아니다. 정작 이런식으로 개발하는 개발자들은 대부분 본인이 Spring Security 를 사용하는줄 알고 어떻게든 해당 프레임워크에 잘못 작성된 코드를 끼워넣기 위해 노력한다.
Spring Security 위에서 동작하는 Filter 를 구현하고 싶다면 AbstractAuthenticationProcessingFilter 추상 클래스를 상속받은 구현체를 만들어야 한다. 그래야 Authentication Object, ProviderManager, AuthenticationProvider 와 같은 Spring Security 가 만들어 놓은 아키텍처 위에서 내가 원하는 협력을 자연스럽게 만들어 나갈 수 있다."
[1] OncePerRequestFilter와 AbstractAuthenticationProcessingFilter의 차이:
- OncePerRequestFilter: Spring Security의 인증 체계와는 독립적으로 동작하며, JWT를 검증하고 SecurityContext에 인증 정보를 설정할 수 있습니다. 이 경우, 인증 절차가 AuthenticationManager나 AuthenticationProvider를 통해 진행되지 않습니다.
- AbstractAuthenticationProcessingFilter: Spring Security의 인증 흐름과 통합되어 작동합니다. attemptAuthentication 메서드를 통해 AuthenticationManager가 호출되며, 이를 통해 등록된 AuthenticationProvider에서 인증 로직이 수행됩니다.
[2] AuthenticationManager, AuthenticationProvider, ProviderManager 의미
- AuthenticationManager:
- Spring Security에서 인증을 총괄하는 인터페이스입니다. 인증 요청을 받아 Authentication 객체로 결과를 반환하거나 인증에 실패하면 예외를 던집니다.
- AuthenticationManager는 단일 책임을 가지고 있지 않고, 다양한 인증 방식을 조합하여 사용할 수 있게 하는 추상화된 인터페이스입니다.
- AuthenticationProvider:
- 구체적인 인증 로직을 구현하는 컴포넌트입니다. AuthenticationManager는 여러 개의 AuthenticationProvider를 사용하여 다양한 인증 방법을 지원할 수 있습니다.
- 각 AuthenticationProvider는 특정 인증 방식(예: 사용자 이름과 비밀번호 인증, JWT 인증, OAuth2 인증 등)을 구현하며, 인증이 가능한지 판단하고 결과를 반환합니다.
- ProviderManager:
- AuthenticationManager의 기본 구현체로, 여러 AuthenticationProvider를 관리합니다.
- 인증 요청이 들어오면 ProviderManager는 등록된 AuthenticationProvider 목록을 순차적으로 검사하여 인증을 처리할 수 있는 AuthenticationProvider를 찾고, 인증이 성공하면 해당 결과를 반환합니다. 만약 모든 AuthenticationProvider가 인증에 실패하면 예외를 던집니다.
[3] JWT 사용할때 OncePerRequestFilter VS AbstractAuthenticationProcessingFilter
만약 최초 로그인시 인증을 필터에서 처리한다면 ProviderManager가 AuthenticationProvider 선택해서 인증 해야하니
AbstractAuthenticationProcessingFilter 사용하나,
나는 최초 인증시 직접적으로 컨트롤러에서 하니 ProviderManager, AuthenticationProvider 다 필요없어서
OncePerRequestFilter 를 사용해도 된다.
[4] UserDetailsImpl과 UserDetailsServiceImpl
* UserDetailsService (gpt답변)
UserDetailsService는 username/password 인증방식을 사용할 때 사용자를 조회하고 검증한 후 UserDetails를 반환합니다. Custom하여 Bean으로 등록 후 사용 가능합니다.
* UserDetails (gpt답변)
검증된 UserDetails는 UsernamePasswordAuthenticationToken 타입의 Authentication를 만들 때 사용되며 해당 인증객체는 SecurityContextHolder에 세팅됩니다. Custom하여 사용가능합니다.
UserDetailsImpl과 UserDetailsServiceImpl 의 경우 이전 프로젝트나 인터넷에 보면 DB에서 해당 사용자 조회해오는데 JWT 방식의 경우 생각해보면 성능을 위해 사용하는데 매번 토큰검증시에도 UserDetailsServiceImpl에 의해 DB 조회하면 왜 사용하나? 생각이 든다.
UserDetailsServiceImpl로 DB 조회후 시큐리티 컨텍스트 홀더에 Authentication 객체에 유저디테일 넣는데, 이거를 하는 이유는 JWT토큰 검증해도 순간 DB에 실제 유저의 권한이나 무엇이 변경 됬을수도 있어서 그런다는데,
생각해보면 그런게 중요한 로직은 그 해당 API 코드에서 직접 유저 정보를 조회해오는게 매번 UserDetailsServiceImpl 로 조회 하는것보다 더 낫다 생각든다.
그러므로 JWT 최초 인증시 컨트롤러에서 직접 인증하고,로그인중 유저 정보 필요시 그 순간만 직접 DB 조회해오는 방식일때는 UserDetailsImpl과 UserDetailsServiceImpl 다 필요 없다 생각든다.
그러므로 토큰 검증후나 최조 인증시 SecurityContextHolder에 Authentication 의 UsernamePasswordAuthenticationToken 넣을때 그래서 나는 UserDetail 안넣고 간단히 이렇게 넣었다.
private Authentication createAuthentication(String email) {
return new UsernamePasswordAuthenticationToken(email, null, List.of());
}
[5] 시큐리티 설정에 http.addFilterBefore()
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrf) -> csrf.disable());
http.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers("").permitAll()
.requestMatchers("/api/users/**","").permitAll()
.anyRequest().authenticated()
);
http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
현재 코드에 addFilterBefore()는 Spring Security의 전체 필터 체인 내에서 지정된 특정 필터 앞에 커스텀 필터를 추가하는 것이다. 따라서, Spring Security가 관리하는 여러 필터들 중에서 지정한 필터의 정확한 위치를 기준으로 새로운 필터를 삽입하는 역할을 한다.
예시를 들어보면
- Spring Security 필터 체인: 예를 들어, Spring Security는 다음과 같은 필터 체인을 가질 수 있다.
- SecurityContextPersistenceFilter
- UsernamePasswordAuthenticationFilter
- BasicAuthenticationFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- addFilterBefore(jwtAuthorizationFilter(), BasicAuthenticationFilter.class)를 사용하면 jwtAuthorizationFilter가 BasicAuthenticationFilter 앞에 추가되므로, 실행 순서는 이렇게 된다.
- SecurityContextPersistenceFilter
- UsernamePasswordAuthenticationFilter
- jwtAuthorizationFilter (추가됨)
- BasicAuthenticationFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
'Spring' 카테고리의 다른 글
CORS 와 PreFlight (0) | 2024.10.11 |
---|---|
@QueryHint 와 @Transaction readOnly 차이 / 비공개 (0) | 2024.04.08 |
Spring_5주차-(2) (0) | 2023.12.04 |
Spring_5주차-(1) (0) | 2023.11.28 |
Spring_4주차-(2) (0) | 2023.11.14 |