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 |
---|