Spring

인증 예외 메시지 커스터마이징

서적자 2023. 7. 25. 00:10

요구사항:

    신규 API를 기존의 프로젝트에 추가하기로 결정하고 작업을 진행

    신규 API의 에러 response는 기존의 것들과 다르게 처리

 

ExceptionHandler를 이용해 처리했으나 인증 예외는 별도 처리가 필요했음

기존 프로젝트는 Spring 1.4.7이고 Oauth2 인증을 사용하고 있었음

 

잘못된 토큰으로 요청하고 debug해본 결과 AuthenticationEntryPoint의 다음 메소드를 override 하기로 결정

void commence(HttpServletRequest var1, HttpServletResponse var2, AuthenticationException var3) throws IOException, ServletException;

 

@Slf4j
public class CustomAuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (toOrRequestMatcher("/a/**", "/b/**").matches(request)) {
            try {
                ResponseEntity<Exception> result = new ResponseEntity<>(new CustomException(CustomErrorCode.E0001), HttpStatus.UNAUTHORIZED);
                new DefaultOAuth2ExceptionRenderer().handleHttpEntityResponse(result, new ServletWebRequest(request, response));
                response.flushBuffer();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            super.commence(request, response, authException);
        }
    }

    private OrRequestMatcher toOrRequestMatcher(String... pattern) {
        return new OrRequestMatcher(Arrays.asList(pattern).stream().map(p -> new AntPathRequestMatcher(p)).toArray(AntPathRequestMatcher[]::new));
    }
}

위와 같이 AuthenticationEntryPoint를 구현

if 절은 예외를 커스터마이징 하기 위한 url 확인

if 절 안의 코드는 상속받은 OAuth2AuthenticationEntryPoint 클래스 내부의 소스를 복사 적용

Exception의 경우 JSON으로 직렬화할 때 여러 속성이 같이 직렬화되므로 ExceptionSerializer를 구현해서 사용

 

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

   private static final String RESOURCE_ID = "rest_api";
   
   @Override
   public void configure(ResourceServerSecurityConfigurer resources) {
      resources.resourceId(RESOURCE_ID).stateless(false);
   }

   @Override
   public void configure(HttpSecurity http) throws Exception {
      http.
      anonymous().disable()
      .requestMatchers().antMatchers("/api/**", "/view/**")
      .and().authorizeRequests()
      .antMatchers("/api/**", "/view/**").authenticated()
      .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
   }
}

위와 같은 설정에서 HttpSecurity 설정에 다음과 같은 코드를 추가하여 CustomAuthenticationEntryPoint를 적용하려 하였으나 실패

 

.and().exceptionHandling().defaultAuthenticationEntryPointFor(new CustomAuthenticationEntryPoint(), new AntPathRequestMatcher("/view/**"))

CustomAuthenticationEntryPoint가 실행되지 않고 OAuth2AuthenticationEntryPoint 가 계속 사용됨

확인해보니 ResourceServerSecurityConfigurer을 커스터마이징 했어야 했음

 

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

   private static final String RESOURCE_ID = "rest_api";
   
   @Override
   public void configure(ResourceServerSecurityConfigurer resources) {
      resources.resourceId(RESOURCE_ID).stateless(false)
            .authenticationEntryPoint(new CustomAuthenticationEntryPoint());
   }

   @Override
   public void configure(HttpSecurity http) throws Exception {
      http.
      anonymous().disable()
      .requestMatchers().antMatchers("/api/**", "/view/**")
      .and().authorizeRequests()
      .antMatchers("/api/**", "/view/**").authenticated()
      .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
   }
}

위와 같이 수정하여 적용완료