Spring Security - Ajax 기반 인증


생각할 거리

Ajax가 비동기 호출이면서 주로 REST API호출인 점을 감안하면 CSR(vue) 와 유사하게 Content-type: application-json 형태일 것 같다.

그리고 이걸 통해서 현재 safe-web에 적용할 수 있는 방안을 찾을 수 있을 것 같다.

일단은 기존의 Form 인증은 스프링 시큐리티가 기본적으로 제공해주는 Security Filter(AuthenticationFilter)인 UsernamePasswordAuthenticationFilter를 사용했었는데 form, get 방식의 request 특성을 고려하여 request.getParameter를 이용한 데이터 추출 방식이었다.

그런데 위 방식은 application-json 타입에서는 사용이 불가능하고, RequestBody를 직접 파싱해줘야 하기 때문에 별도의 AuthenticationFilter를 구현하는 방식을 채택하지 않을까 싶다.


흐름 및 개요

스프링 시큐리티 기반에서는 인증이 필터로 시작해서 필터로 끝난다.


인증 필터 구현

AjaxAuthenticationFilter

  • AbstractAuthenticationProcessingFilter 상속
  • 필터 작동 조건
    • AntPathRequestMatcher(”/api/login”)로 요청 정보와 매칭하고 요청 방식이 Ajax이면 필터 작동
    • Form 방식의 필터와 Ajax 방식과 함께 구성이 가능
  • AjaxAuthenticationToken을 생성하고 AuthenticationManager에게 전달하여 인증 처리
  • Filter 추가
    • http.addFilterBefore(AjaxAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
    • Form 기반의 필터 작동 이전에 먼저 작동하게끔 필터의 순서를 조정하여 추가할 수 있다.

AjaxLoginProcessingFilter(인증 처리 위임 필터)

ajax 인증 요청을 담당할 AuthenticationFilter의 구현체 AjaxLoginProcessingFilter의 코드.

요청 urlContent-Type을 확인하여 request body에 담겨서 전달된 json 형태의 메시지를 객체로 파싱한다.

그 외에는 다른 AuthenticationFilter 처럼

  • 클라이언트가 전달한 ID/PASSWORD 기반으로 Authentication(Token) 생성
  • AuthenticationManager에게 Authentication을 넘기며 인증 처리 위임

AjaxAuthenticationProvider(인증 처리)

AuthenticationManager(ProviderManager)로 부터 전달받은 Authentication을 이용하여 실제 검증 및 인증 처리를 진행한다.

물론 사전에 AuthenticationManagersupports 메서드를 이용하여 해당 AuthenticationProviderAuthentication을 지원하는지 여부를 확인한다.

DispatcherServlet이 RequestHandlerMapper, RequestHandlerAdapter와 협력하는 구조와 유사

  • UserDetailsService로부터 UserDetails를 반환받는다.
    • UserDetailsService가 비즈니스 유즈케이스 이하 계층으로부터 유저의 인증 및 인가와 관련된 정보인 UserDetails를 만들어 반환하는 일종의 어댑터 역할을 한다고 생각하면 될 듯 하다.
  • 클라이언트가 username, password와 함께 전달한 디테일 정보를 authentication으로부터 반환받아 검증한다.
    • 이 정보에는 기본적으로 remoteAddr, SessionID가 저장되어 있다.
    • 필요 시 디테일 정보를 추가가 가능하다.
    • 이 디테일 정보는 Authenticationdetails 필드에 저장이 된다.


인증 핸들러

인증 성공 시, 인증 실패 시 이후 작업을 처리하는 핸들러가 필요하다.

AjaxAuthenticationSuccessHandler

AuthenticationProvider를 거쳐 인증이 성공하게 되면 AuthenticationFilterAuthenticationSuccessHandler를 호출하게 된다.

기존의 view-rendering 방식이 아니라 ajax를 이용한 Http body에 데이터를 담아 Request를 받았기 때문에 마찬가지로 Http Response도 application-json 컨텐츠 타입으로 Response Body 에 응답 데이터를 담아 전달한다.

AjaxAuthenticationFailureHandler

인증 실패 핸들러 역시 마찬가지다.


인증 및 인가 예외 처리

로그인 단계에서 인증 및 인가 예외가 발생하는 경우는 두 가지가 있다.

  • 인증 단계를 거치지 않고(익명의 사용자) 서버의 리소스에 접근하려 하는 경우
    • 인가 예외 발생
    • 인증을 받고 진행하도록 인증 페이지(로그인 페이지)로 이동
  • 인증은 완료가 되었지만 접근하려는 리소스에 대한 권한이 없는 경우
    • 인가 예외 발생
    • 접근 불가 메시지 및 페이지

위의 처리는 인가와 관련된 처리로써 스프링 시큐리티는 이를 FilterSecurityInterceptor, ExceptionTranslationFilter를 이용하여 인가 확인인가 예외 발생을 담당한다.

그리고 이 때 인가 예외 발생 시 처리할 클래스들을 우리가 구현하면 된다.

FilterSecurityInterceptor

FilterSecurityInterceptor라는 클래스는 인가 확인을 하고 만일 인가되지 않은 경우 ExceptionTranslationFilter를 호출하여 인가 예외 처리에 대한 위임을 한다.

ExceptionTranslationFilter

인가 예외에 대한 위임을 받아서 위 두 가지 상황에 따른 인가 예외 처리를 진행한다.

  • 인증 단계를 거치지 않고(익명의 사용자) 서버의 리소스에 접근하려는 경우
    • AuthenticationEntryPoint를 호출하여 인가 예외 처리 진행
    • 우리가 구현
  • 인증은 완료가 되었지만 접근하려는 리소스에 대한 권한이 없는 경우
    • AccessDeniedHandler를 호출하여 인가 예외 처리 진행
    • 우리가 구현

따라서 우리는 인가 예외를 처리할 두 가지(AuthenticationEntryPoint, AccessDeniedHandler) 클래스를 구현하여 등록하면 된다.

AjaxLoginAuthenticationEntryPoint

AjaxAccessDeniedHandler


CustomDSL로 config 실행하기(스킵)

  • AbstractHttpConfigurer
    • 스프링 시큐리티 초기화 설정 클래스
    • 필터, 핸들러, 메서드, 속성 등을 한 곳에 정의하여 처리할 수 있는 편리함 제공
    • public void init(H http) throws Exception - 초기화
    • public void configure(H http) - 설정
  • HttpSecurityapply(C Configurer) 메서드 활용