🐡 Spring Security와 Filter
Filter를 통해 HTTP요청과 응답을 가로채어 인증(Authentication), 인가(Authorization) 과정을 수행한다.
먼저, 스프링 시큐리티가 동작하기 위해서는 Filter가 필요하다. 여기서 Servlet Filter는 Spring의 관리에서 벗어나기 때문에, DI사용에 한계가 있다.
이에 따라 스프링은, 요청을 위임하는 DelegatingFilterProxy 를 만들었다.
즉,
이 두개를 연결해 주는 역할을 하는것이 DelegatingFilterProxy 이다.
"springSecurityFilterChain"
)을 찾는다.doFilter()
메서드를 호출하여 실제 보안 처리를 진행한다.이때 DelegatingFilterProxy가 직접 보안 로직을 수행하는 것이 아닌, "Spring의 필터야, 이 요청 처리 좀 해줘" 하고 넘긴다.
[ Client Request ]
↓
[ Servlet Container Filter Chain ]
↓
[ DelegatingFilterProxy (web.xml 등록) ]
↓ (Spring Bean 호출)
[ FilterChainProxy (Spring Security 설정에 따라 동작) ]
↓
[ Security Filter1 → Security Filter2 → ... → Security FilterN ]
↓
[ DispatcherServlet ]
↓
[ Controller 처리 ]
사용자가 누구인지를 확인하는 과정.
ex) 아이디와 비밀번호로 로그인하여 사용자를 식별.
UsernamePasswordAuthenticationFilter
가 이 요청을 가로챔UsernamePasswordAuthenticationToken
생성AuthenticationManager
에 인증 요청을 위임AuthenticationProvider
가 UserDetailsService
를 통해 사용자 정보를 로드PasswordEncoder
로 비밀번호 비교Authentication
객체를 SecurityContextHolder
에 저장인증된 사용자가 어떤 리소스에 접근할 수 있는지 결정하는 과정.
ex) 일반 사용자는 `/admin/**`에 접근 불가, ROLE_ADMIN 만 접근 가능
FilterSecurityInterceptor
가 요청 URL에 대한 접근 권한 체크Authentication
객체에 포함된 권한(GrantedAuthority
) 목록과 비교AccessDeniedException
발생 → AccessDeniedHandler
로 위임SecurityContext
는 현재 사용자에 대한 보안 정보를 저장하는 객체다.
Spring Security는 이 컨텍스트를 통해 현재 요청이 어떤 사용자에 의해 수행되는지 알 수 있다.
저장 위치:
전략 | 설명 |
---|---|
MODE_THREADLOCAL |
기본 설정. 스레드 단위 보안 컨텍스트 |
MODE_INHERITABLETHREADLOCAL |
자식 스레드에게 보안 컨텍스트 상속 |
MODE_GLOBAL |
전역 공유 컨텍스트 |
Spring Security의 인증 상태를 나타내는 핵심 인터페이스다. 주요 구성 요소는 다음과 같다
메서드 | 설명 |
---|---|
getPrincipal() |
사용자 정보 객체 (보통 UserDetails ) |
getCredentials() |
인증 수단 (보통 비밀번호, 성공 시 null 처리됨) |
getAuthorities() |
권한 목록 (ROLE_USER , ROLE_ADMIN 등) |
isAuthenticated() |
인증 여부 |
Spring Security는 비밀번호를 절대 평문으로 비교하지 않는다.
항상 PasswordEncoder
를 통해 암호화된 상태로 비교해야 한다.
기본적으로 BCryptPasswordEncoder가 권장되며, 단방향 해시 함수로 안전하다.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
인증 시 비밀번호 비교는 이렇게 이뤄진다
String raw = "plain_pw";
String hashed = userDetails.getPassword();
boolean match = passwordEncoder.matches(raw, hashed);