securityInterceptor에 대해서는 권한에 대해 알아볼때 자세히 알아봐야겠다 .
간단히만 정리하면
securityInterceptor 는 FilterSecurityInterceptor 와 MethodSecurityInterceptor로 나뉜다.
FilterSecurityInterceptor는 필터단에서 동작하고 MethodSecurityInterceptor는 메소드에서 선언하고 스프링 어플리케이션안 서블릿단에서 동작한다.
두 클래스는 공통 클래스인 AbstractSecurityIntercepotor를 구현하고 있다 AbstractSecurityIntercepotor의 가장 중요한
멤버 변수는 AccessDecisionManager인데
인증에서 AuthenticationManager가 사용자 인풋을 확인하여 Authentication을 제공해줬다면
AccessDecisionManager은 애플리케이션내의 메타데이터들을 읽어 사용자가 갖고있는 권한을 체크해 주는 역할을 한다.
AccessDecisionManager 밑으로는 AccessVoter들이 있는데 말그대로 특정 요청이 있을때 AccessVoter들을 소집하여 투표하게 하고 요청이 securityInterceptor를 통과할 수 있는지 없는지를 결정한다. 통과에 실패할 시 AccessDeniedException이 발생한다.
FilterSecurityInterceptor
일반적으로 스프링 시큐리티에서 제공하는 필터들은 Filter로 끝나도록 네이밍 되어 있지만 FilterSecurityInterceptor에만 필터지만 Interceptor라는 이름이 붙는다 .
스프링시큐리에서 중요한 개념인 인증과 권한 중 인증을 체크하고 부여하는 securityFilter 들이 있었다면 권한을 체크하는 역할을 하는 것이 securityInterceptor이다 . securityInterceptor는 스프링 어플리케이션안에서 동작한다 . 하지만 FilterSecurityInterceptor의 경우 securityInterceptor역할을 하지만 필터단에서 동작한다. 주로 그룹핑된 요청에 대해 권한체크가 필요할 때 사용된다.
FilterSecurityInterceptor는 권한 체크에 필요한 메타데이터들을 보관하는 있는 레지스트리인
SecurityMetadataSource(인터페이스)를 갖는다
(FilterInvocationSecurityMetadataSource는 FilterSecurityInterceptor에 대한 구현체이고
MethodSecurityInterceptor에 대한 구현체는 MethodSecurityMetadataSource이다.)
위 그림의 permitAll hasAnyRole authenticated 같은 정보들 파싱되어 SecurityMetadataSource에 담긴다.
ExceptionTranslationFilter
인증과 권환 체크에서 발생하는 AuthenticationException과 AccessDeniedException에 대한 Exception을 체크하는 필터이다.
에러 발생한다면 ExceptionTranslationFilter는 해당에러가 AuthenticationException인지 AccessDeniedException인지를
체크하고 AccessDeniedException라면 AuthenticationTrustResolver를 통해 익명사용자인지 리멤버미 사용자인지를 확인한다 .
만약 익명사용자이거나 리멤버미 사용자라면 AuthenticationEntryPoint에게 넘겨 인증을 받고오도록 한다.
반대로 인증을 했지만 권한이 없는 사용자라면 AccessDeniedHandler 로 넘겨 권한 실패 페이지로 넘긴다.
각각 401 (인증실패) , 403(권한없음) 에러에 대응한다.
ExceptionTranslationFilter의 dofilter 메서드
요청이 들어오고 에러가 터지면 IOExcpetion이 아닌 경우에 분석기를 통해서 해당 에러가 authenticationException인지
AccessDeniedException인지를 확인하고 아니라면 rethrow하고
두 개중에 하나라면 SecurityException으로 핸들링하도록 구현되어있다 .
AuthenticationException일 경우
AuthenticationException이라면 아래 그림처럼
sendStartAuthentication을 호출하고 sendStartAuthentication에서는 authenticationEntryPoint.commence를 호출하여 인증 시작페이지로 보내버린다.
authenticationEntryPoint는 기본적으로는 로그인페이지로 보내도록되어있지만 , 어떤 경우에는 로그인페이지로 보내지 않을 수도 있고 , 에러메시지를 경고창으로 띄울 수도 있고 , 에러페이지로 보낼 수도있고 상황이 여러개이기 때문에 인터페이스로 구현되어 있다 .
원한다면 authenticationEntryPoint 커스터마이징하여 해당 상황에 대해 직접 정의할 수 있다. 로그인 성공시에 원래 가려던 경로로 보내줄 수 있도록 requestCache에 reqeust를 저장해 놓는다.
AccessDeniedException인 경우
AccessDeniedException인 경우 해당 사용자가 익명사용자인지 혹은 remeberMe사용자인지를 확인한다.
특이한 점은 remeberMe사용자에 대해서도 익명사용자와 같은 취급을 받는다는 것이다.
어플리케이션에서는 remeberMe로 로그인했을때 만약 권한이 없다면 재로그인을 요구할 것이다
스프링에서 rememberMe토큰을 FullAuthentication토큰과는 다르게 취급하려는 의도인거 같다고 한다.
위의 문제를 회피하기 위한 방법 중 하나는 RememberMeService에서 Authentication을 제공하는 createSuccessfulAuthentication()메서드를 오버라이딩 하여 RememberMeAuthenticationToken이 아닌 UsernamePassordAuthenticationToken으로 발급시켜주는 방법이 있다.
사이트 정책에 맞게 해당 문제에 대해 대응 해야할 것 같다 .
'Spring Security' 카테고리의 다른 글
스프링 시큐리티 공부 15 - WebExpressionVoter 와 PreinvocationAuthorizationAdviceVoter (0) | 2021.07.14 |
---|---|
스프링시큐리티 공부 15 - 권한이란? (0) | 2021.07.08 |
스프링시큐리티 공부 13 - 세션관리 (세션 모니터링) (0) | 2021.07.06 |
스프링시큐리티 공부 12 - 세션관리 (ConcurrentSessionFilter) (0) | 2021.07.06 |
스프링시큐리티 공부 11 - 로그인 유지 (session과 remberme cookie) (0) | 2021.07.06 |