Reputation: 1645
I'm using a custom Spring Security filter which overrides AbstractAuthenticationProcessingFilter but I must have written it incorrectly as it seems to never call the rest of the filter chain. Specifically, I'm relying on the OpenEntityManagerInViewFilter filter to ensure Jackson+Hibernate can handle lazy-loaded objects.
If my web.xml has OpenEntityManagerInViewFilter first, everything works:
<filter>
<filter-name>hibernateFilterChain</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
If I place the springSecurityFilterChain at the top, however, my application behaves as though I didn't specify the OpenEntityManagerInViewFilter at all.
Here is my springSecurity.xml:
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http entry-point-ref="restAuthenticationEntryPoint"
use-expressions="true" create-session="stateless">
<security:custom-filter ref="authenticationTokenProcessingFilter"
position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/**"
access="isAuthenticated()" />
<security:logout />
</security:http>
<bean class="edu.ucdavis.dss.dw.security.CustomTokenAuthenticationFilter"
id="authenticationTokenProcessingFilter">
<constructor-arg type="java.lang.String">
<value>/**</value>
</constructor-arg>
</bean>
<security:authentication-manager>
<security:authentication-provider
user-service-ref="userService"></security:authentication-provider>
</security:authentication-manager>
<bean id="userService" class="edu.ucdavis.dss.dw.services.UserAuthenticationService"></bean>
</beans>
And finally, here is the CustomTokenAuthenticationFilter itself, which may be causing the issues:
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Autowired @Qualifier("org.springframework.security.authenticationManager")
private AuthenticationManager authenticationManager;
public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(new NoOpAuthenticationManager());
setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getParameter("token");
if(token == null) {
throw new AuthenticationServiceException("Token Missing");
}
Authentication authResponse;
try {
authResponse = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
} catch (AuthenticationException e) {
throw new AuthenticationServiceException("Bad Token");
}
return authResponse;
}
}
In summary: I made a custom security filter and it appears not to call any filters which are listed after it. If I remove my custom filter and use something built-in like security:http-basic, it works fine.
Thanks in advance for any help you can offer.
Upvotes: 2
Views: 6618
Reputation: 7812
The AbstractAuthenticationProcessingFilter
class has the following method which can be overridden:
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response)
This method is called before initiating the authentication with the filter (in doFilter
):
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
This method should be overridden by the subclass to check whether it can authenticate the request: it should return false if the token is not present or invalid.
For example:
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
if (!super.requiresAuthentication(request, response)) {
// We're not required to authenticate this request (ant matchers).
return false;
}
if (null == request.getParameter("token")) {
// We can't authenticate this request, because the token is missing.
return false;
}
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
} catch (AuthenticationException e) {
// We can't authenticate this token because an exception occurred.
return false;
}
// We can authenticate this request.
return true;
}
Upvotes: 1
Reputation: 285
What Christopher mentioned in his reply is correct, but I thought it would be better to show some code. For my case, I have implemented custom authentication method by extending UsernamePasswordAuthenticationFilter and also custom AuthenticationSuccessHandler implementation.
In the beginning, I have the implementation as below for the authenticationSuccessHandler to return the response
httpServletResponse.setStatus(HttpServletResponse.
httpServletResponse.setContentType("application/json");
httpServletResponse.getWriter().write(loginSuccessResponse);
httpServletResponse.getWriter().flush();
But, when I turned on debugging using the following:
@EnableWebSecurity(debug = true)
and enable debug logging for org.springframework.security.web.FilterChainProxy.
I realized that the security filter chain stopped at the custom implementation UsernamePasswordAuthenticationFilter. Then, I found Christopher reply and did as what he shared, which is to:
Redirect the response in AuthenticationSuccessHandler to another endpoint:
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.sendRedirect(ApiResourceConstant.POST_LOGIN);
}
}
You can explore on SimpleUrlAuthenticationSuccessHandler too. Then, you should be able to see all the filter chain is fired from the log.
Hope it helps :)
Upvotes: 1
Reputation: 1645
From what I can tell, a Spring authentication filter is meant to authenticate a request and then redirect somewhere, starting a whole new request. This means that the filter chain is purposefully halted. The idea is that, once authenticated and in a new request, an authentication session will already exist so the authentication filter will not have to redirect again. At least, this is my understanding from a little debugging.
In order to get around this behavior, you have to implement your own AbstractAuthenticationProcessingFilter, AuthenticationEntryPoint, AbstractAuthenticationToken, AuthenticationProvider, and SimpleUrlAuthenticationSuccessHandler.
Upvotes: 2
Reputation: 3321
Do you want to try to add
public void doFilter(javax.servlet.ServletRequest req,
javax.servlet.ServletResponse res,
javax.servlet.FilterChain chain)
throws IOException,
javax.servlet.ServletException {
chain.doFilter(req, res);
}
to the CustomTokenAuthenticationFilter class?
Upvotes: 2