AuthenticationManager를 bean등록해야하는 이유 


문제 상황

jwt관련하여 발생하는 모든 에러를 403 에러로 반환함 

토큰을 누락하는 경우에도 403, 토큰이 유효하지 않은 경우에도 403 

개발할때 원인을 찾기 어려울뿐더러 클라이언트에서 구분하기 어려움 

고려할점 - 어디까지 에러 코드를 구분할 것인가 (보안차원에서)

 

authenticationEntryPoint

정의

authenticationEntryPoint: 인증되지 않은 사용자가 보호된 리소스에 접근했을 때 무엇을 할지 정하는 시작 지점

인증되지 않은 사용자란

  • 토큰 없음
  • 토큰 만료
  • 로그인 안함 
요청
 ↓
Security Filter Chain // 필터 체인으로 가장 먼저 호출된다 
 ↓
Authentication 확인 // 인증 정보를 확인한다 토큰 등.. 
 ↓
❌ 인증 정보 없음
 ↓
AuthenticationException 발생 // 인증 정보가 없으므로 예외를 발생시킨다 
 ↓
ExceptionTranslationFilter
 ↓
AuthenticationEntryPoint 호출 ✅

 

AuthenticationEntryPoint를 만들지 않으면 모든 에러가 403으로 내려온다 

왜 이런 현상이 발생할까 ?

 

-> Spring Security는 원래 “웹 사이트용” 프레임워크다

그래서 기본 동작이 인증 안되면 로그인 페이지로 다시 보내도록 기본 설정이 되어있다

그럼 여기서 AuthenticationEntryPoint를 만들지 않았다면 기본 설정대로 LoginUrlAuthenticationEntryPoint를 등록한다 

이 EntryPoint의 역할은 로그인 페이지로 리다이렉트 시키는 일을 한다 

 

전혀문제될게 없어보이지만 api를 구현한다면 보통 json으로 데이터를 주고 받을 것이라고 예상하지만 

스프링 시큐리티는 로그인 페이지로 응답할 것이다 (기본 설정 때문에) 

더보기
더보기

//json을 예상했지만 실제 스프링이 응답으로 보낸것 

 

<html>
  <form>login</form>
</html>

그러면 파싱에러가 날것이다 기대한 타입과 다르므로 

 

따라서 내가 커스텀하게 EntryPoint을 바꾸는것이 authenticationEntryPoint을 사용하는 것과 같다 

 

주의할점

AuthenticationEntryPoint는 Security Filter Chain 안에서 AuthenticationException이 발생했을 때만 호출된다

 

즉 다른 에러를 사용해서 다음과 같이 코드가 작성되어 있었기 때문에 인식을 못했다.

RuntimeException / IllegalStateException는 EntryPoint를 호출하지 않는다 

try {
        Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
        return true;
    } catch (JwtException | IllegalArgumentException e) {
        log.warn("잘못된 JWT 토큰: {}", e.getMessage());
        return false;
    }

 

의문점 1 - EntryPoint 설정을 추가하지 않고 jwt를 예외처리하면 안되나? 

AuthenticationEntryPoint가 필요한 이유는 무엇일까 

예외처리는 기본적으로 호출한 곳의 상위로 throws 예외를 던지기 때문에 globalException에서 처리할 수 없다

jwt필터는 인증 보다 ,컨트롤러 호출 보다도 앞에서 호출된다.

JwtAuthenticationFilter (OncePerRequestFilter)
  ↓
UsernamePasswordAuthenticationFilter
  ↓
AuthorizationFilter
  ↓
ExceptionTranslationFilter

 

여기서 살펴볼 것은 예외처리란 무엇인가이다. 

숫자를 0으로 나눌때와 같은 예외를 들어보자

이는 “이 입력은 내 책임 범위 밖이다 호출자가 처리해라”를 명시하면서 ArithmeticException를 던진다 

 

그런데 jwt를 보자 null,만료,위조 모두 시스템 오류가 아니다. 

다시말해 실패 != 예외임을 헷갈려선 안된다 

jwt를 검증할 수 없다 == 보안 판단을 할 수 없다 

jwt를 검증할 수 없다 != 심각한 오류가 발생했다 

parserBuilder도중 실패 = 라이브러리 오류 = 심각한 오류 발생 = 예외처리 필요

 

즉 스프링 시큐리티 입장에서 jwt검증 실패는 처리할 수 있는 정상 범위니까 예외가 아니라 판단해야 하는 상태라고 본다 

 

의문점 2 - EntryPoint 설정을 추가하지 않고 jwt에서 AuthenticationException을 던지면 되지 않을까? 

필터에서 인증 실패가 발생하면, 바로 다음 단계로 넘어가지 않고 ExceptionTranslationFilter가 잡음

클라이언트 요청 →
  SecurityContextPersistenceFilter →
  JwtFilter / UsernamePasswordAuthenticationFilter →
  ExceptionTranslationFilter →
  기타 인증/인가 필터 →
  DispatcherServlet →
  Controller

 

그리고 AuthenticationManager 내부에서 발생한 에러만 ExceptionTranslationFilter가 catch 

AuthenticationManager authManager = ...;
try {
    authManager.authenticate(token); // <- 이 라인에서 실패하면 AuthenticationException 발생
} catch (AuthenticationException ex) {
    // Security는 이걸 인증 실패로 간주
}

 

ExceptionTranslationFilter의 핵심 로직

try {
    filterChain.doFilter(request, response);  // 다음 필터로 진행
} catch (AuthenticationException ex) {
    authenticationEntryPoint.commence(request, response, ex);  // 인증 실패 처리
} catch (AccessDeniedException ex) {
    accessDeniedHandler.handle(request, response, ex);           // 권한 없음 처리
}

 

필터에서 직접 던지면:

  • filterChain.doFilter() 바깥에서 RuntimeException 발생
  • ExceptionTranslationFilter는 try 안에서 발생한 인증 시도 실패만 보고 catch
  • 그래서 Security 입장에서는 “내가 만든 인증 실패가 아니다” → 그냥 RuntimeException
  • 결과: EntryPoint 호출 안 됨

어째서 바깥에서 발생했는가? 

말 그대로 doFilter를 호출하기 전에 jwt검증을 끝냈다 

그래서 이게 왜 바깥이냐? doFilter 호출로 try-catch를 하기 때문임

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
    FilterChain filterChain) throws ServletException, IOException {

    String jwt = resolveToken(request);

    if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
        Authentication authentication = jwtTokenProvider.getAuthentication(jwt);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    filterChain.doFilter(request, response);
}

 

결론

Spring Security에서 인증 실패를 자동으로 EntryPoint로 연결시키려면 인증 시도가 반드시 AuthenticationManager를 거쳐야 함.

 

 

 

AccessDeniedHandler: 로그인 했지만(=인증 통과) 권한 부족한 경우

 

 

 

'TIL' 카테고리의 다른 글

TIL/ 260112  (0) 2026.01.13
TIL/251219  (0) 2025.12.19
TIL/ 연관관계 매핑  (0) 2025.10.21
TIL / RESTful API  (0) 2025.10.17

+ Recent posts