본문 바로가기
Spring/security

Spring Security 예외 처리

by 옹알이옹 2024. 4. 22.
목차

1. AccessDenied 예외 처리

2. 비밀번호 불일치 예외 처리

 


 1. AccessDenied 예외 처리

 

Security에서 기본적으로 권한이 없는 리소스에 요청을 보내면 403(AccessDenied) 예외를 발생시킨다.
하지만 response에 메시지 등 정보를 넣어주기 위해서는 아래와 같은 처리가 필요하다.

 

1. 우선 Security Config에서 exceptionHandling 메서드를 통해 커스텀 한 handler를 등록해 준다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http
        .csrf(AbstractHttpConfigurer::disable)
        .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/login", "/signup/**").permitAll()
                .requestMatchers("/member/**").hasRole("USER")
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
        );

    http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class)
        .httpBasic(Customizer.withDefaults());

    http
        .exceptionHandling(exceptionHandling ->
                exceptionHandling.accessDeniedHandler(jwtAccessDeniedHandler)
        );

    return http.build();
}

 

2. 커스텀 한 핸들러는 아래와 같이 구성한다.

@Component
@RequiredArgsConstructor
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

    private final HandlerExceptionResolver handlerExceptionResolver;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {

        handlerExceptionResolver.resolveException(request, response, null, accessDeniedException);
    }

}
  • 중요한 것은 HandlerExceptionResolver를 통해 예외를 발생시키지 않으면 ControllerAdviser에서 잡지 못한다.
  • 물론 해당 코드에서 response 객체에 직접 예외 메시지를 넣고 리턴해줄 수 있지만 글로벌 예외 처리 부분에서
    공통적으로 잡기 위해 사용하였다.

ControllerAdvice 코드

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(AccessDeniedException.class)
    protected ResponseEntity<ErrorResponse> accessDeniedException(AccessDeniedException ex) {

        ErrorResponse errorResponse = new ErrorResponse("권한이 없습니다.");
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse);
    }
}

 

 

 2. 비밀번호 불일치 예외 처리

로그인 시 비밀번호 불일치 시 예외를 발생시키는 주체는 authenticationManagerBuilder의 authenticate이다.
해당 메서드 실행 시 UserDetailService 구현체의 loadUserByUsername(String name) 를 실행시켜 DB에서 조회 한 뒤
입력한 비밀 번호와 암호화된 DB의 비밀번호를 조회하게 되는데 이 과정에서 해당 예외를 발생시킨다.

 

참고로 해당 에러는 최종적으로 response 메시지에서 확인할 수 없어 application.yaml에서 root log level을 debug로 바꾼 뒤 콘솔에서 확인 할 수 있다.

비밀번호 불일치 에러

디버깅 모드로 해당 예외를 추적하면 최종적으로 해당 코드가 예외를 발생시키는 것을 확인할 수 있다.

@Override
@SuppressWarnings("deprecation")
protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
   if (authentication.getCredentials() == null) {
      this.logger.debug("Failed to authenticate since no credentials provided");
      throw new BadCredentialsException(this.messages
         .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
   }
   String presentedPassword = authentication.getCredentials().toString();
   if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      this.logger.debug("Failed to authenticate since password does not match stored value");
      throw new BadCredentialsException(this.messages
         .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
   }
}

 

여기서 발생한 BadCredentailsException을 controllerAdviser에 등록하면 해당 예외를 잡아서 커스텀할 수 있다.

 

ControllerAdvice 코드

@ExceptionHandler(BadCredentialsException.class)
protected ResponseEntity<ErrorResponse> credentialException(BadCredentialsException ex) {

    ErrorResponse errorResponse = new ErrorResponse("비밀번호 확인 바람");
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}

 

응답 결과

비밀 번호 불일치 응답

 

반응형

'Spring > security' 카테고리의 다른 글

Spring Security6 JWT구성  (1) 2024.04.22