cannot call sendError() after the response has been committed

created at 09-13-2021 views: 1

frame

springboot+IDEA+spring security oauth2

background

When the user logs in to obtain the token, and the user name or password authentication fails, the onAuthenticationFailure method is executed, and the error code and error message have been returned normally, but the console still throws an exception. The error message is as follows.

2021-09-12 07:16:25.490  WARN 964 --- [nio-8090-exec-1] d.c.h.CustomAuthenticationFailureHandler : If authentication fails, the return message is:{"code":1100,"info":"User does not exist."}
2021-09-12 07:16:25.500 ERROR 964 --- [nio-8090-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/api/sso] threw exceptionjava.lang.IllegalStateException: Cannot call sendError() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:456) ~[tomcat-embed-core-9.0.52.jar:9.0.52]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:120) ~[tomcat-embed-core-9.0.52.jar:4.0.FR]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:120) ~[tomcat-embed-core-9.0.52.jar:4.0.FR]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:126) ~[spring-security-web-5.5.2.jar:5.5.2]
    at org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure(SimpleUrlAuthenticationFailureHandler.java:86) ~[spring-security-web-5.5.2.jar:5.5.2]
    at com.datong.liran.datongssoserver.config.handle.CustomAuthenticationFailureHandler.onAuthenticationFailure(CustomAuthenticationFailureHandler.java:39) ~[classes/:na]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication(AbstractAuthenticationProcessingFilter.java:342) ~[spring-security-web-5.5.2.jar:5.5.2]

The error code and error message have been returned normally, but the console still reports an error, which is not the result I want, what should I do? There is no way, only to continue to reform, let him learn how to be a man in different categories, and show his true color as a programmer. Oh, by the way, what does the custom exception handling code look like?

@Component("CustomAuthenticationFailureHandler")
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.reset();//Reset the response, otherwise an error will be reportedgetWriter() has already been called for this response
//        PrintWriter printWriter = httpServletResponse.getWriter();Can't use getWrite(), can't return error correctly
        OutputStream out = response.getOutputStream();
        response.setContentType("application/json;charset=UTF-8");
        Result result = Result.error(1100,exception.getMessage(),null);
        String rspBodyStr = JSONObject.toJSONString(result);//Entity class to string
         logger.warn("Authentication failed, the return message is: "+rspBodyStr);
        out.write(rspBodyStr.getBytes(StandardCharsets.UTF_8));//Strings are written to the output stream
        out.close();
        super.onAuthenticationFailure(request,response,exception);
    }
}

Analysis

According to the error log, the preliminary judgment should be repeated submission or repeated return, but only the response is involved here, so you can only go to the parent class (onAuthenticationFailure) to find it. Open it, and the code is as follows.

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (this.defaultFailureUrl == null) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Sending 401 Unauthorized error since no failure URL is set");
            } else {
                this.logger.debug("Sending 401 Unauthorized error");
            }

            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        } else {
            this.saveException(request, exception);
            if (this.forwardToDestination) {
                this.logger.debug("Forwarding to " + this.defaultFailureUrl);
                request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
            } else {
                this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
            }

        }
    }

Obviously, no matter the defaultFailureUrl is null, and the sendError() method is executed again, it should be the same as the previous setting, and this method also reports an error in the error log. Now that the error code and error message I want has been returned, I just don’t adjust the parent. Class method. After a try, as expected, the console no longer reported an error and returned normally.

solution

No longer call the parent class method, comment super.onAuthenticationFailure(request, response, exception); statement.

Ok, get it done, and start sharing the joy of success.

Please log in to leave a comment.