2017-12-06 19 views
8

REST 서비스를 개발 중입니다. JSON을 사용하며 문제가 발생하면 미리 정의 된 JSON 객체를 반환해야합니다. 기본 봄 응답은 다음과 같습니다스프링 MVC (또는 스프링 부트). 401 Unauthorized 또는 403 Forbidden과 같은 보안 관련 예외에 대한 사용자 정의 JSON 응답

{ 
    "timestamp": 1512578593776, 
    "status": 403, 
    "error": "Forbidden", 
    "message": "Access Denied", 
    "path": "/swagger-ui.html" 
} 

내가 (A 스택 트레이스 및 추가 예외 관련 정보)를 자신의 하나이 기본 JSON을 대체합니다.

스프링은 기본 동작을 덮어 쓸 수있는 편리한 방법을 제공합니다. 사용자 정의 예외 핸들러를 사용하여 @RestControllerAdvice bean을 정의해야합니다. 이

@RestControllerAdvice 
public class GlobalExceptionHandler { 
    @ExceptionHandler(value = {Exception.class}) 
    public ResponseEntity<ExceptionResponse> unknownException(Exception ex) { 
    ExceptionResponse resp = new ExceptionResponse(ex, level); // my custom response object 
    return new ResponseEntity<ExceptionResponse>(resp, resp.getStatus()); 
    } 
    @ExceptionHandler(value = {AuthenticationException.class}) 
    public ResponseEntity<ExceptionResponse> authenticationException(AuthenticationExceptionex) { 
     // WON'T WORK 
    } 
} 

사용자 정의처럼 ExceptionResponse 객체는 특별한 메시지 변환기를 사용하여 Spring에 의해 JSON으로 변환됩니다.

문제 IS, InsufficientAuthenticationException 등이 보안 예외 @ExceptionHandler 같이 주석의 방법에 의해 차단 될 수 없다. 이러한 종류의 예외는 Spring MVC 디스패처 서블릿이 입력되고 모든 MVC 핸들러가 초기화되기 전에 발생합니다.

사용자 지정 필터를 사용하여이 예외를 가로 채고 자체 JSON 직렬화를 처음부터 만들 수 있습니다. 이 경우 스프링 MVC 인프라의 나머지 부분과 완전히 독립적 인 코드를 얻을 수있다. 이거는 좋지 않습니다.

내가 찾은 해결책은 효과가있는 것 같지만 미친 것처럼 보입니다.

@Configuration 
public class CustomSecurityConfiguration extends 
WebSecurityConfigurerAdapter { 

@Autowired 
protected RequestMappingHandlerAdapter requestMappingHandlerAdapter; 

@Autowired 
protected GlobalExceptionHandler exceptionHandler; 

@Override 
protected void configure(HttpSecurity http) throws Exception { 

    http.authorizeRequests() 
     .anyRequest() 
     .fullyAuthenticated(); 

    http.exceptionHandling() 
     .authenticationEntryPoint(authenticationEntryPoint()); 
} 

public AuthenticationEntryPoint authenticationEntryPoint() { 
    return new AuthenticationEntryPoint() { 
     @Override 
     public void commence(HttpServletRequest request, HttpServletResponse response, 
       AuthenticationException authException) throws IOException, ServletException { 

      try { 
       ResponseEntity<ExceptionResponse> objResponse = exceptionHandler.authenticationException(authException); 

       Method unknownException = exceptionHandler.getClass().getMethod("authenticationException", AuthenticationException.class); 

       HandlerMethod handlerMethod = new HandlerMethod(exceptionHandler, unknownException); 

       MethodParameter returnType = handlerMethod.getReturnValueType(objResponse); 

       ModelAndViewContainer mvc = new ModelAndViewContainer(); // not really used here. 

       List<HttpMessageConverter<?>> mconverters = requestMappingHandlerAdapter.getMessageConverters(); 

       DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response); 

       HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(mconverters); 

       processor.handleReturnValue(objResponse, returnType, mvc, webRequest); 
      } catch (IOException e) { 
       throw e; 
      } catch (RuntimeException e) { 
       throw e;      
      } catch (Exception e) { 
       throw new ServletException(e); 
      } 
     } 
    }; 
} 

이보다 더 나은 모습 (메시지 컨버터에서 MIME 형식 협상 등을 구축 봄과) 봄 직렬화 파이프를 사용하는 방법이 있나요?

+0

여기 좀 더 우아한 해결책이 있지만, 원래 예외를 @ExceptionHandler가 "catch"할 수있는 예외로 래핑하는 것과 같습니다. https://stackoverflow.com/questions/43632565/exceptionhandler-for- a-pre-controller-filter-spring-security/43636386 – spekdrum

+0

http://www.baeldung.com/spring-security-custom-access-denied-page –

답변

0

스프링은 아래와 같이 정의 처리기 일 구현 AccessDeniedExceptionHTTP 403

경우 맞춤 JSON 응답을 달성하기 위하여 이하의 절차에 따라 이용 될 수 AccessDeniedHandler 통해 운반 예외 부정기 지원을 제공 /deny를 제외 할 수 있도록 -

public class CustomAccessDeniedHandler implements AccessDeniedHandler { 

    @Override 
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exc) 
     throws IOException, ServletException { 
     System.out.println("Access denied .. "); 
     // do something 
     response.sendRedirect("/deny"); 
    } 
} 

다음하는 설정이 핸들러의 빈을 생성하고 보안 예외 핸들러 (중요 봄에 공급 인증로부터 다른 요청되는 것) 무한 /deny에 대응하는 핸들러 쓰기 컨트롤러 클래스

@Bean 
public AccessDeniedHandler accessDeniedHandler(){ 
    return new CustomAccessDeniedHandler(); 
} 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http.cors().and() 
      // some other configuration 
      .antMatchers("/deny").permitAll() 
      .and() 
      .exceptionHandling().accessDeniedHandler(accessDeniedHandler()); 
} 

다음 오류)을 해결 계속 단순히 새로운 SomeException의 인스턴스 (또는 다른 Exception로서 적합를 슬로우 각각의 핸들러는 @RestControllerAdvice에서 인터셉트됩니다.

@GetMapping("/deny") 
public void accessDenied(){ 
    throw new SomeException("User is not authorized"); 
} 

더 많은 정보가 필요하면 주석으로 알려주십시오.

0

다음 구성이 작동해야한다고 생각합니다. 시도해보십시오.

@Autowired 
private HandlerExceptionResolver handlerExceptionResolver; 

public AuthenticationEntryPoint authenticationEntryPoint() { 
    return new AuthenticationEntryPoint() { 
     @Override 
     public void commence(HttpServletRequest request, HttpServletResponse response, 
          AuthenticationException authException) throws IOException, ServletException { 

      try { 
       handlerExceptionResolver.resolveException(request, response, null, authException); 
      } catch (RuntimeException e) { 
       throw e; 
      } catch (Exception e) { 
       throw new ServletException(e); 
      } 
     } 
    }; 
} 
1

콩 클래스

@Component public class AuthenticationExceptionHandler implements AuthenticationEntryPoint, Serializable 

를 만들고 commence() method을 무시하고 SecurityConfiguration 클래스의 예를 아래에

ObjectMapper mapper = new ObjectMapper(); 
String responseMsg = mapper.writeValueAsString(responseObject); 
response.getWriter().write(responseMsg); 

@Autowire AuthenticationExcetionHandler 같은 객체 매퍼를 사용하여 JSON 응답을 생성하고 configure(HttpSecurity http) method에 선

아래에 추가
 http.exceptionHandling() 
      .authenticationEntryPoint(authenticationExceptionHandler) 

이렇게하면 커 스톰 json 응답 401/403을 보낼 수 있습니다. 위와 동일하게 AccessDeniedHandler을 사용할 수 있습니다. 이것이 문제 해결에 도움이되는지 알려주십시오.