본문 바로가기

개발/Spring

[Spring] Security의 인가(Authorization) 실패 시 처리

기존 프로젝트에서 스프링 시큐리티 사용에 있어, 인가 실패 시

스프링이 제공하는 기본 예외를 반환하고 있어, 내가 원하는 Json 형태로의 반환을 위해 추가로 커스텀이 필요했다.

 

당연히 스프링 시큐리티에서 예외처리 커스텀이 가능한 기능을 지원하고 있다.

AccessDeniedHandler 인터페이스를 구현받고 handle 메소드를 재정의 한 후,

시큐리티 설정에 추가해주면 된다.

 

우선 Jwt와 시큐리티 예외처리를 동일한 형식으로 처리하기 위한 Response Writer 용 static method를 만들었다.

public class CommonWriter {

    private static String convertToJson(Object object) {
        try {
            return new ObjectMapper().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new IllegalStateException("Could not convert object to JSON", e);
        }
    }
    
    public static void exceptionWriter(HttpServletResponse response, Exception exception) throws IOException {

        final ExceptionType type;

        if (exception instanceof AuthenticationException) {
            type = ExceptionType.NOT_AUTHORIZED_TOKEN;
        } else if (exception instanceof AccessDeniedException){
            type = ExceptionType.ACCESS_DENIED;
        } else {
            type = ExceptionType.UNKNOWN_EXCEPTION_TYPE;
        }

        Map<String, Object> errorDetails = new HashMap<>();

        errorDetails.put("statusCode", type.getStatus().value());
        errorDetails.put("status", type.getStatus().name());
        errorDetails.put("errorCode", type.getErrorCode());
        errorDetails.put("message", type.getMessage());

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write(convertToJson(errorDetails));
    }
}

그 후 AccessDeniedHandler를 구현한 Custom 클래스를 만들고 Writer를 호출한다.

아래와 같이  handler 메소드에서 exceptionWriter를 호출했다.

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        CommonWriter.exceptionWriter(response, accessDeniedException);
    }
}

exceptionWriter에서는 jwt 토큰인증 에러와 스프링 시큐리티 예외에 대한 Exception 인스턴스를 구분하여,

미리 지정해 둔 예외를 반환하도록 처리하였다.

그 후 Security의 설정(filterChain) 메소드에 추가해준다.

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler) //추가된 부분
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

exceptionHandling().accessDenieHandler()에서 정의한 부분을 추가해준다.

 

이후로 @ControllerAdvice로 요청 예외를 처리할 수 없는 인스턴스에 대한 처리는 exceptionWriter를 통해 수행할 수 있을 것으로 보인다.

 

 

참고 : https://mighty96.github.io/til/access-authentication/