웹 프로그래밍/[ Spring Security ]

[ Spring Security ] 05. 인증/인가 API의 예외처리

kim.svadoz 2021. 12. 12. 01:17
728x90
반응형
인프런에서 진행하는 정수원 강사님의 "스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security" 강의를 보고 학습을 위해 개인적으로 추가/정리한 글임을 알립니다.

인증/인가 API


ExceptionTranslationFilter

FilterSecurityInterceptor(맨 마지막에 위치)가 이를 호출한다

ExceptionTranslationFilter 필터는 아래 두가지 예외를 처리하고 있다.

  • AuthenticationException
    • 인증 예외 처리
      1. AuthenticationEntryPoint 호출
        • 로그인 페이지 이동
        • 401 오류 코드 전달 등
      2. 인증 예외가 발생하기 전의 요청 정보를 저장
        • RequestCache : 사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내 오는 캐시 메커니즘을 제공
        • *SavedRequest * : 사용자가 요청했던 request 의 파라미터 값들, 그 당시의 헤더값 들이 저장
        이 둘은 모두 인터페이스 이며 세부 기능을 하는 구현체가 존재한다.
  • AccessDeniedException
    • 인가 예외 처리
      • AccessDeniedHandler 에서 예외 처리하도록 제공
      얘도 인터페이스 이며 세부 기능을 하는 구현체가 존재한다.

image-20211121222802392

유저가 /user 자원 접근을 시도하고 있다면 FilterSecurityInterceptor에서 엄밀히 말해서는 익명사용자 이기 때문에 인증 예외가 아니라, 인가 예외로 빠지게 된다.

이 ExceptionTranslationFilter가 인가 예외로 보내긴 하지만 AccessDeniedHandler 로 곧바로 보내지 않고, HttpSessionRequestCache로 보내버린다.

 

따라서, 쉽게 생각해서 인증 예외의 과정으로 보내어진다고 보면 된다.

인증예외파트에서는 두 가지 일을 하게 된다.

  1. 인증 실패 이후 처리
    • Security Context안에 있는 인증 객체를 null로 만든다.
    • AuthenticationEntryPoint 에서 다시 인증할 수 있도록 로그인 페이지로 보내버리고
  2. 사용자의 요청관련 정보 저장
    • 예외가 발생하기 이전의 유저정보를 저장한다.
    • 이 정보는 DefaultSavedRequest 객체 안에 저장되고 이 객체는 Session에 저장한다.
    • 그 역할을 HttpSessionRequestCache 클래스가 수행한다.

 

만약에 유저가 /user 페이지가 아니라, /admin 자원에 접근하려고 한다면 인가예외가 발생한다.

ExceptionTranslationFilter는 AccessDeniedException이 발생하고 AccessDeniedHandler 를 호출해서 그 안에서 후속처리를 처리한다.
(보통은 /denied페이지를 redirect 한다. 또는 이 자원에 다시금 접근할 수 있도록 조정할 수 있는 처리를 하게 된다.)

 

> 예외처리 과정

protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint())     // 인증실패 시 처리
            .accessDeniedHandler(accessDeniedHandler())                // 인가실패 시 처리
    ;
}
protected void configure(HttpSecurity http) throws Exception {
    http
                .successHandler(new AuthenticationSuccessHandler() { // 로그인 성공 시 호출하는 핸들러
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        System.out.println("authentication: " + authentication.getName());
                        httpServletResponse.sendRedirect("/");

                        // 이미 인증예외 필터에서 RequestCache에 캐시정보가 세션에 담겨 있으므로 불러와서 사용가능하다.
                        RequestCache requestCache = new HttpSessionRequestCache(); 
                        SavedRequest savedRequest = requestCache.getRequest(httpServletRequest, httpServletResponse);
                        String redirectUrl = savedRequest.getRedirectUrl();
                        httpServletResponse.sendRedirect(redirectUrl);
                    }
                })
    ;

    http
                .exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                         // login만 인증을 받지 않아도 그 자원에 접근할 수 있어야 한다. -> 인가처리를 따로 해줘야 한다.
                        response.sendRedirect("/login");
                    }
                })
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                        response.sendRedirect("/denied");
                    }
                })

        ;
}

 

그리고 이 때 sendRedirect 할 때는 Spring-Security에서 제공하는 페이지가 아니기 때문에 직접 페이지를 만들어줘야 한다.

image-20211121230810332

728x90
반응형