簡體   English   中英

Spring 引導自定義錯誤 object 導致 InsufficientAuthenticationException

[英]Spring Boot Custom error object for InsufficientAuthenticationException

我已經為我的 API 實現了身份驗證,它按預期工作。 用戶首先訪問 auth api 通過傳遞用戶名和密碼來獲取令牌。 這個 api 返回一個令牌。 然后,用戶通過傳遞令牌來調用安全 API。 此問題是當用戶傳遞無效令牌或未傳遞令牌時,從 Spring 引導返回默認錯誤 object。 我想自定義這個 object 並且為此,我編寫了一個自定義異常處理程序擴展 ResponseEntityExceptionHandler 但這沒有被觸發,因為在 controller 啟動之前引發了異常。

@ExceptionHandler(value = {InsufficientAuthenticationException.class})
public final ResponseEntity<Object> 
authenticationException(InsufficientAuthenticationException ex) {
    List<String> details = new ArrayList<>();
    details.add("Authentication is required to access this resource");
    ErrorResponse error = new ErrorResponse("error", "Unauthorized", details);
    return new ResponseEntity(error, HttpStatus.FORBIDDEN);
}

AuthenticationProvider 負責根據客戶端在 header 中發送的身份驗證令牌找到用戶。 這就是我們基於 Spring 的令牌身份驗證提供程序的樣子:

@Component
public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
 @Autowired
 CustomerService customerService;

 @Override
 protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
  //
 }

 @Override
 protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {

  Object token = usernamePasswordAuthenticationToken.getCredentials();
  return Optional
   .ofNullable(token)
   .map(String::valueOf)
   .flatMap(customerService::findByToken)
   .orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token));
 }

令牌認證過濾器負責從header獲取認證過濾器並調用認證管理器進行認證。 這是身份驗證過濾器的樣子:

public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    AuthenticationFilter(final RequestMatcher requiresAuth) {
        super(requiresAuth);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {

        Optional tokenParam = Optional.ofNullable(httpServletRequest.getHeader(AUTHORIZATION)); //Authorization: Bearer TOKEN
        String token= httpServletRequest.getHeader(AUTHORIZATION);
        token= StringUtils.removeStart(token, "Bearer").trim();
        Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token);
        return getAuthenticationManager().authenticate(requestAuthentication);

    }

    @Override
    protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        chain.doFilter(request, response);
    }
}

Spring 安全配置如下所示:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


 private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
  new AntPathRequestMatcher("/api/**")
 );

 AuthenticationProvider provider;

 public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
  super();
  this.provider = authenticationProvider;
 }

 @Override
 protected void configure(final AuthenticationManagerBuilder auth) {
  auth.authenticationProvider(provider);
 }

 @Override
 public void configure(final WebSecurity webSecurity) {
  webSecurity.ignoring().antMatchers("/token/**");
 }

 @Override
 public void configure(HttpSecurity http) throws Exception {
  http.sessionManagement()
   .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
   .and()
   .exceptionHandling()
   .and()
   .authenticationProvider(provider)
   .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class)
   .authorizeRequests()
   .requestMatchers(PROTECTED_URLS)
   .authenticated()
   .and()
   .csrf().disable()
   .formLogin().disable()
   .httpBasic().disable()
   .logout().disable();

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

 @Bean
 AuthenticationFilter authenticationFilter() throws Exception {
  final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
  filter.setAuthenticationManager(authenticationManager());
  //filter.setAuthenticationSuccessHandler(successHandler());
  return filter;
 }

 @Bean
 AuthenticationEntryPoint forbiddenEntryPoint() {
  return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN);
 }

    @Autowired
    private HandlerExceptionResolver handlerExceptionResolver;

    public AuthenticationEntryPoint authenticationEntryPoint() {
        log.error("in authenticationEntryPoint");
        return new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response,
                                 AuthenticationException authException) throws IOException, ServletException {
                log.error("in commence");
                try {
                    log.error(authException.getLocalizedMessage());
                    handlerExceptionResolver.resolveException(request, response, null, authException);
                } catch (RuntimeException e) {
                    throw e;
                } catch (Exception e) {
                    throw new ServletException(e);
                }
            }
        };
    }
}

PS:參考https://www.javadevjournal.com/spring/securing-a-restful-web-service-with-spring-security/

由於您正在自定義AbstractAuthenticationProcessingFilter ,因此您還可以自定義它的AuthenticationFailureHandler ,它會在attemptAuthentication()拋出AuthenticationException時被調用。 然后,您可以在那里處理錯誤。

一個例子是:

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler{

    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException {

          //create your custom error object
         CustomError error = xxxxx;


         response.setStatus(HttpStatus.FORBIDDEN.value());
         response.setContentType("application/json");
         response.setCharacterEncoding("UTF-8");

         // Format the custom error object as JSON string , for example using Jackson :
         ObjectMapper mapper = new ObjectMapper();
         response.getWriter().write(mapper.writeValueAsString(error));
    }

}

並配置使用它:

 @Bean
 AuthenticationFilter authenticationFilter() throws Exception {
   final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
   filter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
   return filter;
 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM