处理Spring Boot Resource Server中的安全性exception

如何获取自定义ResponseEntityExceptionHandlerOAuth2ExceptionRenderer来处理纯安全性在纯资源服务器上引发的exception?

我们实施了一个

 @ControllerAdvice @RestController public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { 

因此,只要资源服务器上出现错误,我们就希望它能够回答

 { "message": "...", "type": "...", "status": 400 } 

资源服务器使用application.properties设置:

 security.oauth2.resource.userInfoUri: http://localhost:9999/auth/user 

对我们的auth服务器进行身份validation和授权请求。

但是,任何弹簧安全性错误都将绕过我们的exception处理程序

  @ExceptionHandler(InvalidTokenException.class) public ResponseEntity<Map> handleInvalidTokenException(InvalidTokenException e) { return createErrorResponseAndLog(e, 401); } 

并生产

 { "timestamp": "2016-12-14T10:40:34.122Z", "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "/api/templates/585004226f793042a094d3a9/schema" } 

要么

 { "error": "invalid_token", "error_description": "5d7e4ab5-4a88-4571-b4a4-042bce0a076b" } 

那么如何配置资源服务器的安全exception处理呢? 我找到的只是如何通过实现自定义OAuth2ExceptionRenderer来自定义Auth服务器的OAuth2ExceptionRenderer 。 但我找不到将其连接到资源服务器的安全链的位置。

我们唯一的配置/设置是这样的:

 @SpringBootApplication @Configuration @ComponentScan(basePackages = {"our.packages"}) @EnableAutoConfiguration @EnableResourceServer 

如前面的评论中所述,安全框架在到达MVC层之前拒绝了该请求,因此@ControllerAdvice不是一个选项。

Spring Security框架中有3个接口可能会引起关注:

  • org.springframework.security.web.authentication.AuthenticationSuccessHandler
  • org.springframework.security.web.authentication.AuthenticationFailureHandler
  • org.springframework.security.web.access.AccessDeniedHandler

您可以创建每个接口的实现,以便自定义为各种事件发送的响应:成功登录,登录失败,尝试访问权限不足的受保护资源。

以下将在失败的登录尝试时返回JSON响应:

 @Component public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException { response.setStatus(HttpStatus.FORBIDDEN.value()); Map data = new HashMap<>(); data.put("timestamp", new Date()); data.put("status",HttpStatus.FORBIDDEN.value()); data.put("message", "Access Denied"); data.put("path", request.getRequestURL().toString()); OutputStream out = response.getOutputStream(); com.fasterxml.jackson.databind.ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(out, data); out.flush(); } } 

您还需要使用安全框架注册您的实现。 在Java配置中,如下所示:

 @Configuration @EnableWebSecurity @ComponentScan("...") public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class).logout().deleteCookies("JESSIONID") .logoutUrl("/api/logout").logoutSuccessHandler(logoutSuccessHandler()).and().formLogin().loginPage("/login") .loginProcessingUrl("/api/login").failureHandler(authenticationFailureHandler()) .successHandler(authenticationSuccessHandler()).and().csrf().disable().exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint()).accessDeniedHandler(accessDeniedHandler()); } /** * @return Custom {@link AuthenticationFailureHandler} to send suitable response to REST clients in the event of a * failed authentication attempt. */ @Bean public AuthenticationFailureHandler authenticationFailureHandler() { return new RestAuthenticationFailureHandler(); } /** * @return Custom {@link AuthenticationSuccessHandler} to send suitable response to REST clients in the event of a * successful authentication attempt. */ @Bean public AuthenticationSuccessHandler authenticationSuccessHandler() { return new RestAuthenticationSuccessHandler(); } /** * @return Custom {@link AccessDeniedHandler} to send suitable response to REST clients in the event of an attempt to * access resources to which the user has insufficient privileges. */ @Bean public AccessDeniedHandler accessDeniedHandler() { return new RestAccessDeniedHandler(); } } 

如果你正在使用@EnableResourceServer ,你也可以在@Configuration类中找到扩展ResourceServerConfigurerAdapter而不是WebSecurityConfigurerAdapter便利。 通过执行此操作,您可以通过重写configure(ResourceServerSecurityConfigurer resources)并在方法内使用resources.authenticationEntryPoint(customAuthEntryPoint())来注册自定义AuthenticationEntryPoint

像这样的东西:

 @Configuration @EnableResourceServer public class CommonSecurityConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.authenticationEntryPoint(customAuthEntryPoint()); } @Bean public AuthenticationEntryPoint customAuthEntryPoint(){ return new AuthFailureHandler(); } } 

还有一个很好的OAuth2AuthenticationEntryPoint可以扩展(因为它不是最终的),并且在实现自定义AuthenticationEntryPoint时可以部分重用。 特别是,它添加了“WWW-Authenticate”标头以及与错误相关的详细信息。

您无法使用Spring MVCexception处理程序注释,例如@ControllerAdvice因为Spring安全filter在Spring MVC之前就已经开始了。

OAuth2ExceptionRenderer用于授权服务器。 正确的答案可能就像在这篇文章中详述的那样处理它(也就是说,忽略它是oauth并像对待任何其他Spring安全认证机制一样对待它): https : //stackoverflow.com/a/26502321/5639571

当然,这将捕获与oauth相关的exception(在到达资源端点之前抛出),但是资源端点中发生的任何exception仍然需要@ExceptionHandler方法。

Spring 3.0以后,您可以使用@ControllerAdvice (Class Class)并从CustomGlobalExceptionHandler扩展org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler

 @ExceptionHandler({com.test.CustomException1.class,com.test.CustomException2.class}) public final ResponseEntity customExceptionHandler(RuntimeException ex){ return new ResponseEntity(new CustomErrorMessage(false,ex.getMessage(),404),HttpStatus.BAD_REQUEST); }