Spring ControllerAdvice和身份验证/授权异常处理

时间:2016-05-06 20:06:21

标签: java spring spring-mvc spring-security spring-boot

在我的Spring Boot应用程序中,我有一个以下的Web安全配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off   
        http
            .headers().frameOptions().disable()
            .and()
                .antMatcher("/**").authorizeRequests()
                .antMatchers("/actuator/health").permitAll()
                .antMatchers("/actuator/**").hasAuthority(Authority.Type.ROLE_ADMIN.getName())
                .antMatchers("/login/**").permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .loginProcessingUrl("/login")
                    .failureUrl("/login?error").permitAll()
            .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/login?logout")
            .and()
                .csrf().csrfTokenRepository(csrfTokenRepository()).ignoringAntMatchers("/login/**")
            .and()
                .addFilterBefore(corsFilter, ChannelProcessingFilter.class)
                .addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class)
                .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class)
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
        // @formatter:on
    }

....

}

ControllerAdvice

@ControllerAdvice
public class GlobalControllerExceptionHandler  {

    private static final String ERROR = "error";

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public Map<String, ErrorResponse> handleException(Exception exception) {
        return createResponseError(exception);
    }

    private Map<String, ErrorResponse> createResponseError(Exception exception) {
        final Map<String, ErrorResponse> responseError = new HashMap<String, ErrorResponse>();
        responseError.put(ERROR, new ErrorResponse(exception.getMessage()));
        return responseError;
    }

}

现在,当我试图通过匿名用户访问我的安全API网址时,我收到以下错误:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri May 06 22:59:05 EEST 2016
There was an unexpected error (type=Forbidden, status=403).
Access Denied

我需要在我的ControllerAdvice(为了返回JSON错误响应)中处理此类错误,而不是这个页面,这对所有其他异常都有效,但仅适用于经过身份验证的用户。

如何通过GlobalControllerExceptionHandler处理此身份验证/授权错误?

1 个答案:

答案 0 :(得分:4)

ControllerAdvice旨在帮助Controller个类,ExceptionHandler用于处理Controller引发的异常。 AuthenticationExceptionAccessDeniedException通常由Spring安全AbstractSecurityInterceptor引发。所以,我猜你将无法使用AuthenticationException来捕获这些ControllerAdvice,因为ExceptionTranslationFilter已经捕获它们并将它们转换为适当的HTTP响应。

更好的方法是在exceptionHandling中使用WebSecurityConfigurerAdapter。使用它,您可以配置AuthenticationEntryPointAccessDeniedHandler。在这里,我正在为拒绝访问的案例返回403 Forbidden,为丢失的身份验证令牌案例返回401 Unauthorized

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception { 
        http.exceptionHandling()
            .accessDeniedHandler((request, response, accessDeniedException) -> {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
            })
            .authenticationEntryPoint((request, response, authException) -> {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            });
    }

}

使用exceptionHandling,您可以将ExceptionTranslationFilter配置为使用accessDeniedHandler来处理AccessDeniedExceptionauthenticationEntryPoint AuthenticationException。查看ExceptionTranslationFilter以获得有关该过程的更多信息。

如果您不喜欢:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri May 06 22:59:05 EEST 2016
There was an unexpected error (type=Forbidden, status=403).
Access Denied

要想对其进行自定义,您应该为ErrorController提供一个实现,并为错误返回Map<String, ErrorResponse>