Spring Security:如何返回503服务不可用?

时间:2016-12-30 19:00:36

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

我有一个使用Spring Security进行身份验证的RESTful Web服务。我已将自定义AuthenticationProvider注册到我的应用程序,该应用程序将其authenticate()业务逻辑委派给远程服务,该服务可能不可用。当远程服务不可用时,我希望呼叫能够*返回503 Service Unavailable

Spring Security会按预期处理403 Forbidden,但我想在某个地方添加503逻辑。 AuthenticationProvider接口可以抛出运行时AuthenticationException,但它及其后代由ProviderManager处理。

这是我的WebSecurityConfigurer:

// how do I hook this into 'http'???
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

protected void configure(HttpSecurity http) throws Exception {
  http
    .authenticationProvider(myAuthenticationProvider)
    .httpBasic()
    .and()
    .exceptionHandling()
        .authenticationEntryPoint(myAuthenticationEntryPoint)
        .accessDeniedHandler(myAccessDeniedHandler)
    .and()
    .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
    .authorizeRequests()
    ...

如果我有自定义HttpSecurity,如何配置503以返回所需的AuthenticationProvider

* - 诚然,这是一个极端的案例。

2 个答案:

答案 0 :(得分:1)

为您的Web服务身份验证注册 AbstractAuthenticationProcessingFilter ,并从那里尝试进行身份验证。您可以在同一过滤器类中配置失败处理程序。

public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
private static Logger logger = LoggerFactory.getLogger(AjaxLoginProcessingFilter.class);

private final AuthenticationSuccessHandler successHandler;
private final AuthenticationFailureHandler failureHandler;

private final ObjectMapper objectMapper;

public AjaxLoginProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler, 
        AuthenticationFailureHandler failureHandler, ObjectMapper mapper) {
    super(defaultProcessUrl);
    this.successHandler = successHandler;
    this.failureHandler = failureHandler;
    this.objectMapper = mapper;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, IOException, ServletException {
    if (!HttpMethod.POST.name().equals(request.getMethod()) || !WebUtil.isAjax(request)) {
        if(logger.isDebugEnabled()) {
            logger.debug("Authentication method not supported. Request method: " + request.getMethod());
        }
        throw new AuthMethodNotSupportedException("Authentication method not supported");
    }

    LoginRequest loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);

    if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isBlank(loginRequest.getPassword())) {
        throw new AuthenticationServiceException("Username or Password not provided");
    }

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());

    return this.getAuthenticationManager().authenticate(token);
}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
        Authentication authResult) throws IOException, ServletException {
    successHandler.onAuthenticationSuccess(request, response, authResult);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException failed) throws IOException, ServletException {
    SecurityContextHolder.clearContext();
    failureHandler.onAuthenticationFailure(request, response, failed);
}

}

<强> LoginRequest.java

public class LoginRequest {
private String username;
private String password;

@JsonCreator
public LoginRequest(@JsonProperty("username") String username, @JsonProperty("password") String password) {
    this.username = username;
    this.password = password;
}

public String getUsername() {
    return username;
}

public String getPassword() {
    return password;
}

}

身份验证提供程序中的身份验证失败并实现AuthenticationFailureHandler,然后更改响应状态。

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

    response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());

 }

您可以指定何时在故障处理程序中抛出哪个状态。

答案 1 :(得分:1)

我能够通过使用2个自定义组件来解决它:

  • MyUnavailableException(扩展AuthenticationException
  • Custom503AuthenticationEntryPoint
public class Custom503AuthenticationEntryPoint implements AuthenticationEntryPoint {

    public void commence(HttpServletRequest request, HttpServletResponse response,
      AuthenticationException ex) throws IOException, ServletException {
        if (ex instanceof MyUnavailableException) {
            response.sendError(HttpStatus.SERVICE_UNAVAILABLE.value(), HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase());
        }
...