I just ran into this a couple days ago.
The culprit is the SavedRequestAwareWrapper that the SecurityContextHolderAwareRequestFilter wraps around the the request before the NtlmProcessingFilter accesses it.
Here's what happens:
- The browser makes the first request for a restricted resource. Note that this first request has no Authentication header.
- The SecurityContextHolderAwareRequestFilter wraps the request with a SavedRequestAwareWrapper .
- The SavedRequestAwareWrapper constructor saves the request as a session attribute for later use.
- The server returns a 401 message indicating that a NTLM authentication attempt should be made.
Second Request (Type 2)
- The browser then makes a second request with an appropriate Authentication header.
- The SecurityContextHolderAwareRequestFilter again wraps the request with a SavedRequestAwareWrapper .
- The NtlmProcessingFilter attempts to get the the Authorization header from the request...
This is where the trouble starts. At this point, the NtlmProcessingFilter isn't accessing the raw request, but instead accesses the SavedRequestAwareWrapper. When getHeader() is called on the SavedRequestAwareWrapper, it checks to see if there is a saved request in the session, and if so it returns the header value of that request instead of the current. Remember the initial request without the Authorization header is currently saved, and since there is no Authorization header, the getHeader() method returns null - which is exactly not what you want.
There are a number of ways to get around this. One is to not use the SecurityContextHolderAwareRequestFilter at all, but that doesn't sound like an option for you. Another is to modify the getHeader() method on the SavedRequestAwareWrapper to make an exception for Authorization header requests. Like this...
But if you REALLY want to be non-intrusive, and protect yourself somewhat from upgrades, you can instead make your own modified copy of SavedRequestAwareWrapper, and mess around with the Spring context to make sure that requests are wrapped with your class instead of the standard one.
else if (savedRequest == null)
To do this, you need to explicitly define your own SecurityContextHolderAwareRequestFilter in your spring context, and inject your custom SavedRequestAwareWrapper.
If you just did this, you would get an error for attempting to insert 2 filters with the same position number (the default one and the one you are defining here). You can get around this by preventing the default from starting up, so that the one that you are using is used. To do this, you need to set the servlet-api-provision attribute to the http tag to false:
<bean id="servletApiSupportFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter">
<property name="wrapperClass" value="sebastians.cool.SavedRequestAwareWrapper" />
I don't know if this is what the original Spring Security folks intended as a work around, but hopefully that gets you pointed in the right direction!
<sec:http servlet-api-provision="false" access-decision-manager-ref="accessDecisionManager" entry-point-ref="ntlmEntryPoint">
<sec:intercept-url pattern="/access_denied.jsp" filters="none" />
<sec:intercept-url pattern="/**" access="ROLE_USER" />