[Spring Security] AccessDeniedException

Jung·2021년 12월 21일
0

TIL

목록 보기
68/77
post-thumbnail

Service에서 반환 타입을 ResponseEntity로 해서 글 작성자가 아닌 사용자가 해당 글을 수정하려고 할 때 403 에러를 응답하는 코드를 사용했다.

하지만 Service에서는 ResponseEntity로 반환을 하게 되면 비즈니스 로직을 처리할 뿐만 아니라 http status code도 응답하는 2가지 역할을 하고 있던 것이다. 이는 객체 지향 설계 원칙(SOLID)에서 SRP를 위배하는 행위이고, 확장성으로 봤을 때도 좋지 않기 때문에 OCP도 위반했다고 볼 수 있다.

그래서 ResponseEntity를 바로 뺐다.
기존에 사용했던 return new ResponseEntity<>(...); 부분을 throw new AccessDeniedException(...);로 변경했다.

여기서 AccessDeniedException스프링 시큐리티에서 제공하는 Exception이다.

처음에는 AccessDeniedException 대신에 커스텀으로 Exception을 하나 만들고 Global Exception Handler를 생성해서 해당 Exceptionthrow 되었을 때 Global Exception Handler를 통해 원하는 응답을 주려고 했다.

하지만 스프링 시큐리티에서 제공하는 AccessDeniedException을 발견하고 이 예외를 사용했다.

AccessDeniedException이 던져졌을 때 받을 수 있는 handler를 구현해야 한다.
AccessDeniedHandler 인터페이스를 implement 해서 구현체를 생성한다. 그리고 handle 메서드를 Override 해서 원하는 응답을 정의한다.

예시

@Component
@Slf4j
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        log.error("AccessDeniedException", accessDeniedException);
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "권한이 없습니다.");
    }
}

이 구현체를 Spring Security Config에 설정해야 한다.

예시

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AccessDeniedHandlerImpl accessDeniedHandler;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
        ...
    }
}

403 응답

{
    "timestamp": 시간,
    "status": 403,
    "error": "Forbidden",
    "path": "/test" // 에러 발생한 경로
}
profile
97kim.github.io

0개의 댓글