Spring Security의 동작은 사실상 Filter로 동작한다고 해도 무방합니다.
다양한 필터들이 존재하는데 이 Filter들은 각자 다른 기능을 하고 있습니다.
이런 Filter들은 제외할 수도 있고 추가할 수도 있습니다. 필터에 동작하는 순서를 정해줘서 원하는대로 유기적으로 동작할 수 있습니다.
필터의 종류는 많지만 많이 쓰이는 필터는 아래와 같습니다.
•
SecurityContextPersistenceFilter
•
BasicAuthenticationFilter
•
UsernamePasswordAuthenticationFilter
•
CsrfFilter
•
RememberMeAuthenticationFilter
•
AnonymousAuthenticationFilter
•
FilterSecurityInterceptor
•
ExceptionTranslationFilter
→ FilterChainProxy Class에서 doFilterInternal에 break point를 걸어서 디버깅해보면 알 수 있습니다.
→필터 순서는 FilterOrderRegistration에서 확인 가능합니다.
•
순서가 100씩 증가한다 WHY? 개발자가 Filter를 중간중간 끼워 넣으라고
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter는 보통 두번째로 실행되는 필터입니다.
SecurityContext를 찾아와서 SecurityContextHolder에 넣어주는 역할을 하는 Filter 입니다.
만약에 SecurityContext를 찾았는데 없다면 그냥 새로 하나 만들어줍니다.
HttpSession
SecurityContextPersistenceFilter는 SecurityContext가 있으면 그걸 가져오고 없으면 새로 만든다고 했습니다. 가져올 수 있는 방법은 많지만, 기본적으로 HttpSession에서 가져옵니다.
BasicAuthenticationFilter
직접 필터를 적용해보면 따로 로그인이라는 과정을 하지 않았는데도 터미널에서 일회성으로 페이지를 불러올 수 있었습니다.
이처럼 우리가 로그인이라고 부르는 과정이 없어도 username:user123 / password:pass123 라는 로그인 데이터를 인코딩해서 모든 요청에 포함해서 보내면 BasicAuthenticationFilter는 이걸 인증합니다.
그렇기 때문에 세션이 필요 없고 요청이 올때마다 인증이 이루어집니다 (즉 stateless합니다)
이런 방식은 요청할 때마다 아이디와 비밀번호가 반복해서 노출되기 때문에 보안에 취약합니다.
그렇기 때문에 이 filter를 사용할 때는 반드시 https를 사용하도록 권장됩니다.
→ BasicAuthenticationFilter를 사용하지 않을 것이라면 명시적으로 disable 시켜주는것이 좋습니다.
UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter는 Form 데이터로 username, password 기반의 인증을 담당하고 있는 필터입니다.
ProviderManager(AuthenticationManager)
인자로 받은 authentication이 유효한지 확인하고 authentication을 반환합니다.
인증하면서 계정에 문제가 있는 것이 발견되면 AuthenticationException을 throw할 수 있습니다.
AuthenticationManager는 authenticate 하나만 구현하면 됩니다.
AuthenticationManager를 구현한 클래스가 ProviderManager입니다.
ProviderManager는 Password가 일치하는지, 계정이 활성화 되어있는지를 확인한 뒤 authentication을 반환합니다.
AbstractUserDetailsAuthenticationProvider
... authenticate(...)...{
...
user = retrieveUser(...) //유저를 가져오는 부분
...
this.preAuthenticationChecks.check(user) // 유효한지 체크하는 부분
}
Java
복사
CsrfFilter
Csrf Attack을 방어하는 Filter입니다.
CsrfFilter는 Csrf Token을 사용하여 위조된 페이지의 악의적인 공격을 방어합니다.
정상적인 페이지는 Crsf Token이 있을 것이고
위조된 페이지는 Crsf Token이 없거나 잘못된 Csft Token을 가지고 있습니다.
따라서 정상적인 페이지에는 Crsf Token 값을 알려줘야 하는데 Thmeleaf에서는 페이지를 만들때 자동으로 Csrf Token을 넣어줍니다.
따로 추가하지 않았는데 아래와 같은 코드가 form tag안에 자동으로 생성됩니다.
<input type=”hidden” name=”_csrf” value=”687sdfsdgdfg …”/>
Csrf Filter는 자동으로 활성화되어있는 Filter지만 명시적으로 On 하기 위해서는 http.csrf(); 코드를 추가합니다.
◈ 템플릿 엔진(Thymeleaf, JSP)등을 사용하여 서버 측에서 전체 HTML을 생성하는 구조가 아닌
REST API의 앤드포인트에 의존하는 구조라면 REST API는 HTTP 형식을 따르기 때문에 무상태이며 서버쪽의 세션이나 브라우저 쿠키에 의존하지 않습니다.
API는 CSRF 공격을 받을 가능성이 존재하지 않기때문에
대부분의 REST API를 사용하는 경우 CSRF를 비활성화하고 있습니다.
AnonymousAuthenticationFilter
인증이 안된 유저가 요청을 하면 Anonymous(익명) 유저로 만들어 Authentication에 넣어주는 필터입니다. 인증되지 않았다고 하더라도 Null을 넣는게 아니라 기본 Authentication을 만들어 주는 개념으로 보면 됩니다. 다른 Filter에서 Anonymous유저인지 정상적으로 인증된 유저인지 분기 처리를 할 수 있습니다
http.anonymous().principal("anonymousUser");
Java
복사
FilterSecurityInterceptor
Fiter중에 하나로 전 필터들로부터 넘어온 authentication의 내용을 기반으로 최종 인가 판단을 내립니다. 그렇기 때문에 대부분의 경우에는 필터중에 뒤쪽에 위치합니다.
먼저 인증(Authentication)을 가져오고 만약에 인증에 문제가 있다면 AuthenticationException을 발생합니다.
인증에 문제가 없다면 해당 인증으로 인가를 판단합니다.
이때 인가가 거절된다면 AccessDeniedException을 발생하고 승인된다면 정상적으로 필터가 종료됩니다.
ExceptionTranslationFilter
앞서 본 FilterSecurityInterceptor에서 발생할 수 있는 두가지 Exception을 처리해주는 필터입니다.
참고 자료 및 영상
한 번에 끝내는 Spring 완.전.판 초격차 패키지 Online - Part 1. Spring Framework