본문 바로가기

Development/Diary

[개발 일기][Spring]Spring Security에서 AccessDeniedHandler 와AuthenticationEntryPoint

Spring Security는 유저에 대한 인증 및 권한처리를 가능하게 해주는 spring 보안 프레임워크입니다.

 

저는 프로젝트를 진행하면서 @RestControllerAdvice를 사용해

전역적으로 예외 처리를 하도록 하였으나, 기대한 HTTP status code와 에러 메시지와는 달리

403 Fobidden만 응답받을 뿐이었습니다.

이 문제는 User가 로그인을 하지 않은 채, 서비스의 접근할 경우 

발생한 예외였습니다. 즉 인증되지 않은 클라이언트가 서버에 요청을 보냈을 때의 발생한 상황이었습니다.

 

조사해 보니 Spring Security의 Filter Chain으로 발생한 예외는 서블릿 필터 단계에 속하는 부분이기 때문에 @RestControllerAdvice와 같은 어노테이션으로 예외 처리를 불가능하다는 사실을 알았습니다.

그래서 Spring Security가 발생시키는 Exception Handler를 따로 구현해야 할 필요가 있었습니다.

 

Spring Security에선 AccessDeniedHandler interface와 AuthenticationEntryPoint interface가 존재합니다.

AccessDeniedHandler는 서버에 요청을 할 때 액세스가 가능한지 권한을 체크후 액세스 할 수 없는 요청을 했을 시 동작되고,

AuthenticationEntryPoint는 인증이 되지않은 유저가 요청을 했을 때 동작됩니다.

제가 참여한 프로젝트에선 User의 권한보다 인증의 비중이 더 높아, AuthenticationEntryPoint만 따로 구현해주었습니다.

package com.thesurvey.api.exception;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

/**
 * Handles authentication errors that occur during the Spring Security filter chain, such as when a
 * user attempts to access a secured endpoint without authentication credentials.
 */
public class AuthenticationEntryPointHandler implements AuthenticationEntryPoint {

    // method is called by the Spring Security filter chain when an authentication error occurs.
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("text/plain;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write("권한이 없습니다.");
    }
}

 

public class SecurityConfiguration {

    private final AuthenticationProvider authenticationProvider;

    private final SessionFilter sessionFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        return http
            .csrf().disable()
            .cors().and()
            .authorizeRequests()
                .antMatchers(
                    "/v2/api-docs/**",
                    "/v3/api-docs/**",
                    "/configuration/**",
                    "/swagger-ui.html",
                    "/swagger-ui/**",
                    "/docs/**"
                ).permitAll()
                .antMatchers("/admin/**").hasAuthority("ADMIN")
                .antMatchers("/surveys/**").authenticated()
                .antMatchers("/users/**").authenticated()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .anyRequest().permitAll()
            .and()
            .exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPointHandler())
            .and()
            ...

 

AuthenticationEntryPoint interface를 구현한 AuthenticationEntryPointHandler 클래스를 만들고

.exceptionHandling()에 .authenticationEntryPoint(new AuthenticationEntryPointHandler())를 추가해 주었습니다.

 

참고 자료

https://velog.io/@park2348190/Spring-Security의-Unauthorized-Forbidden-처리

 

Spring Security의 Unauthorized, Forbidden 처리

Spring Security를 적용해서 HTTP 요청에 대해 인증 및 인가를 적용할 경우 시큐리티 필터 체인에 의해 인증 여부나 인가 여부에 따라 요청이 수락되거나 거절된다. 그런데 필터 체인 특성상 이런 Sprin

velog.io

https://kim-jong-hyun.tistory.com/m/36