处理 Spring Boot 资源服务器中的安全异常

新手上路,请多包涵

我怎样才能让我的自定义 ResponseEntityExceptionHandlerOAuth2ExceptionRenderer 来处理纯资源服务器上 Spring 安全引发的异常?

我们实施了一个

@ControllerAdvice
@RestController
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

所以每当资源服务器出现错误时,我们希望它用

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

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

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

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

然而,任何 spring 安全错误将始终绕过我们的异常处理程序

    @ExceptionHandler(InvalidTokenException.class)
    public ResponseEntity<Map<String, Object>> 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"
}

那么如何为资源服务器配置安全异常处理呢?我所找到的只是关于如何通过实现自定义 OAuth2ExceptionRenderer 来自定义 Auth Server 的示例。但是我找不到在哪里将它连接到资源服务器的安全链。

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

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

原文由 Pete 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 614
2 个回答

正如之前评论中所述,请求在到达 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<String, Object> 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();
  }
}

原文由 Alan Hay 发布,翻译遵循 CC BY-SA 4.0 许可协议

In case if you’re using @EnableResourceServer , you may also find convenient to extend ResourceServerConfigurerAdapter instead of WebSecurityConfigurerAdapter in your @Configuration class.通过这样做,您可以通过覆盖 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”标头。

原文由 Vladimir Salin 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题