I have a Custom Spring Security filter which is getting called Multiple times and I am not able to figure out why. I searched it and tried adding FilterRegistrationBean
as some post suggested but still my Secuirty filter gets called multiple times and throws below error -
20:57:49.975 [http-nio-8888-exec-2] DEBUG c.s.m.security.RESTSecurityFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@fb70fff6: Principal: srib; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@43458: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: BC1D5AB21EE4586F2A76C7901F1F953F; Granted Authorities: ROLE_USER, ROLE_ADMIN
20:57:50.220 [http-nio-8888-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Cannot call sendError() after the response has been committed] with root cause
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472)
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129)
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129)
at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:115)
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129)
at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:115)
at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpRequestHandler.java:488)
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:53)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at xxxxxxxxxxxxxxxxxxxxxx.RESTSecurityFilter.successfulAuthentication(RESTSecurityFilter.java:83)
Below are my config and filter -
RESTSecurityConfig
@Configuration
@EnableWebSecurity
@Order(1)
public class RESTSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public RESTSecurityFilter authenticationFilter() throws Exception {
RESTSecurityFilter authenticationFilter = new RESTSecurityFilter("/");
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFilter;
}
@Bean
public RESTAuthenticationProvider authenticationProvider() {
return new RESTAuthenticationProvider();
}
@Bean
public FilterRegistrationBean registration() {
RESTSecurityFilter authenticationFilter = new RESTSecurityFilter("/");
FilterRegistrationBean registration = new FilterRegistrationBean(authenticationFilter);
registration.setEnabled(false);
return registration;
}
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().addFilterAfter(authenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
}
RESTSecurityFilter
public class RESTSecurityFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger log = LoggerFactory.getLogger(RESTSecurityFilter.class);
private static final String ACCESS_KEY_PARAMETER_NAME = "x-access-key";
private static final String SIGNATURE_PARAMETER_NAME = "x-signature";
private static final String NONCE_PARAMETER_NAME = "x-nonce";
private static final String TIMESTAMP_PARAMETER_NAME = "x-timestamp";
private static final String SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
protected RESTSecurityFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String accessKey = getHeaderValue(request, ACCESS_KEY_PARAMETER_NAME);
String signature = getHeaderValue(request, SIGNATURE_PARAMETER_NAME);
String nonce = getHeaderValue(request, NONCE_PARAMETER_NAME);
String timestamp = getHeaderValue(request, TIMESTAMP_PARAMETER_NAME);
String message = accessKey + ":" + nonce + ":" + timestamp;
String hashSignature = null;
try {
hashSignature = HMacUtility.calculateHmac(message, SECRET_KEY);
log.info("hashSignature : {}", hashSignature);
}
catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
AbstractAuthenticationToken authRequest = createAuthenticationToken(accessKey,
new RESTCredentials(hashSignature, signature));
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
// return; // <--- If uncommented, error goes away but response gets stuck in infinite loop
}
private String getHeaderValue(HttpServletRequest request, String headerParameterName) {
return (request.getHeader(headerParameterName) != null) ? request.getHeader(headerParameterName) : "";
}
private AbstractAuthenticationToken createAuthenticationToken(String apiKeyValue, RESTCredentials restCredentials) {
return new RESTAuthenticationToken(apiKeyValue, restCredentials);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return true;
}
}
It's because Spring Boot picks up any Filter
exposed as a @Bean
and adds it to the ServletContext
. By exposing it as a @Bean
and by adding it to the Spring Security Filter Chain, you've effectively registered it twice.
Likely, you can simply remove the @Bean
annotation from your authenticationFilter()
method.