본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


Spring Security

스프링시큐리티 공부 14 - FilterSecurityInterceptor 와 ExceptionTranslationFilter

반응형

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 예시

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으로 발급시켜주는 방법이 있다.  

 

사이트 정책에 맞게 해당 문제에 대해 대응 해야할 것 같다 . 

 

 

 

 

 

반응형