NTLM with Spring Security 2.0

Posted on June 5, 2008

A lot of users are having trouble when dealing with NTLM based authentication with Spring Security. The underlying NTLM support is built on top of JCIFS (jcifs.samba.org), an open source client library that implements the CIFS/SMB networking protocol in Java.

NTLM authentication allows the login credentials of a Windows user, who is logged on into a domain, to be automatically passed to your browser.
NTLM is a Microsoft-developed protocol providing single sign-on capabilities to web applications. It allows a web server to automatically discover the username of a browser client when that client is logged into a Windows domain and is using an NTLM-aware browser. A web application can then reuse the user’s Windows credentials without having to ask for them again.

This only works for Internet Explorer. When using Firefox, you will be prompted with an authentication prompt where you can enter your username and password. You can enable NTLM authentication also in Firefox, by doing the following steps:

  • Type “about:config” in the address bar of Firefox
  • You will see all settings of Firefox, but you need to find the key “network.automatic-ntlm-auth.trusted-uris”.
  • Enter the hostnames like: “host1.domain.com, host2.domain.com”
    or just “.domain.com” to list them all at once

Once you have setup your project with the correct dependencies and libraries, we are ready to start configuring our application context. You need spring-security-core and spring-security-ntlm as project dependencies in order to get it working.


NOTE: It is better to separate your application context files into multiple files, in order to focus the configuration on the core parts of your system.

First we will add the NTLM filter itself:

<bean id="ntlmFilter" class="org.springframework.security.ui.ntlm.NtlmProcessingFilter">
  <property name="stripDomain" value="true"/>
  <property name="defaultDomain" value="domain.mediasoft.be"/>
  <property name="netbiosWINS" value="domain"/>
  <property name="authenticationManager" ref="providerManager"/>
</bean>

The value of the default domain should be the hostname of the SMB server that will be used to authenticate HTTP clients. It is preferred though, to use a WINS server over a specific domain controller.

Next, we add an Exception Translation Filter and pass it the entry point

<bean id="exceptionTranslationFilter"
    class="org.springframework.security.ui.ExceptionTranslationFilter">
  <property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
</bean>

We also need an entry point for our NTLM filter, define it like this:

<bean id="ntlmEntryPoint"
    class="org.springframework.security.ui.ntlm.NtlmProcessingFilterEntryPoint">
  <property name="authenticationFailureUrl" value="/access_denied.jsp"/>
</bean>

Our filter security intercepter can be defined as follows:

A filter security interceptor needs to be in place to perform security handling of HTTP resources:

<bean id="filterSecurityInterceptor"
    class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
  <property name="authenticationManager" ref="providerManager"/>
  <property name="accessDecisionManager" ref="accessDecisionManager"</property>
  <property name="objectDefinitionSource">
    <value>
      CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
      PATTERN_TYPE_APACHE_ANT
      /access_denied.jsp=ROLE_ANONYMOUS
      /**=ROLE_USER
    </value>
  </property>
</bean>

As referenced in the above code block, we need to specify a provider manager to specify our authorization managers:

<bean id="providerManager"
    class="org.springframework.security.providers.ProviderManager">
  <property name="providers">
    <list> <ref bean="daoAuthenticationProvider"/> </list>
  </property>
</bean>

In this example, we will simply use an in-memory userservice to perform the lookup of the actual users allowed to this application. I just provided two test users in this usecase.

<bean id="daoAuthenticationProvider"
    class="be.mediasoft.spring.security.UserDetailsAuthenticationProvider">
  <property name="userDetailsService">
    <ref local="memoryUserDetailsService"/>
  </property>
</bean>

<bean id="memoryUserDetailsService"
    class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
  <property name="userMap">
    <value>
      fox=PASSWORD,ROLE_USER
      administrator=PASSWORD,ROLE_ADMIN
    </value>
  </property>
</bean>

We also need to specify an access decision mechanism. In this case we will just use the plain unanimous voting mechanism:

We’ll just use an UnanimousBased decision manager that requires all voters to abstain or grant access

<bean id="accessDecisionManager"
    class="org.springframework.security.vote.UnanimousBased">
  <property name="allowIfAllAbstainDecisions" value="false"/>
  <property name="decisionVoters">
    <list> <bean id="roleVoter" class= "org.springframework.security.vote.RoleVoter"/> </list>
  </property>
</bean>

Configuring web.xml
When your context configuration is complete, we can now add the following filter declaration to our web.xml file:

<filter>
  <filter-name>_filterChainProxy</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>_filterChainProxy</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

This provides a hook into the Spring Security web infrastructure.

Virtual filter chain
We need to apply all defined filters in a virtual filter chain, so that they can be applied automatically to the requests in the right order.

<bean id="filterSecurityInterceptor"
    class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
  <property name="authenticationManager" ref="providerManager"/>
  <property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
  <property name="objectDefinitionSource">
    <value>
      CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
      PATTERN_TYPE_APACHE_ANT
      /access_denied.jsp=ROLE_ANONYMOUS
      /**=ROLE_USER
    </value>
  </property>
</bean>

Mind the order of these entries as the NTLM filter needs to be defined after the exception translation filter as this filter is based on exceptions to do NTLM handshaking etc.

Our http session context integration filter is defined as follows:

<bean id="httpSessionContextIntegrationFilter"
    class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
  <property name="contextClass" value="org.springframework.security.context.SecurityContextImpl"/>
</bean>

The custom user details provider is implemented as follows:

public class UserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
  private UserDetailsService userDetailsService;
  protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    UserDetails loadedUser;
    try {
      loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    } catch (DataAccessException repositoryProblem) {
      throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
    }
    if (loadedUser == null) throw new AuthenticationServiceException("User cannot be null");
    return loadedUser;
  }
  public UserDetailsService getUserDetailsService() {
    return userDetailsService;
  }
  public void setUserDetailsService(UserDetailsService userDetailsService) {
    this.userDetailsService = userDetailsService;
  }
}

TO BE COMPLETED: Spring 2.0 configuration

<security:authentication-manager alias="_authenticationManager"/>

<security:authentication-provider>
  <security:user-service>
    <security:user name="fox" password="PASSWORD" authorities="ROLE_USER, ROLE_ADMIN"/>
    <security:user name="administrator" password="PASSWORD" authorities="ROLE_USER,ROLE_ADMIN"/>
  </security:user-service>
</security:authentication-provider>

<bean id="userDetailsAuthenticationProvider"
    class="be.mediasoft.spring.security.UserDetailsAuthenticationProvider">
  <security:custom-authentication-provider/>
</bean>

<bean id="ntlmEntryPoint" class="org.springframework.security.ui.ntlm.NtlmProcessingFilterEntryPoint">
  <property name="authenticationFailureUrl" value="/access_denied.jsp"/>
</bean>

<bean id="ntlmFilter" class="org.springframework.security.ui.ntlm.NtlmProcessingFilter">
  <security:custom-filter position="NTLM_FILTER"/>
  <property name="stripDomain" value="true"/>
  <property name="defaultDomain" value="domain.mediasoft.be"/>
  <property name="netbiosWINS" value="domain"/>
  <property name="authenticationManager" ref="_authenticationManager"/>
</bean>

<bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter">
  <property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
</bean>

<security:http access-decision-manager-ref="accessDecisionManager" entry-point-ref="ntlmEntryPoint">
  <security:intercept-url pattern="/access_denied.jsp" filters="none"/>
  <security:intercept-url pattern="/**" access="ROLE_USER"/>
</security:http>

<bean id="accessDecisionManager" class="org.springframework.security.vote.UnanimousBased">
  <property name="allowIfAllAbstainDecisions" value="false"/>
  <property name="decisionVoters">
    <list>
      <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"/>
    </list>
  </property>
</bean>

» Filed Under Spring

Comments

17 Responses to “NTLM with Spring Security 2.0”

  1. Waseem on June 19th, 2008 19:55

    Is this sample completed ? I am really looking forward to use it in my authentication process for our website for internal users

  2. FoX on June 19th, 2008 20:59

    Yes it is, except the Spring 2.0 namespace configuration. But you can perfectly use the normal Spring config without a problem.

  3. joobha on June 27th, 2008 10:07

    Could you add to the article ziped working project? Or send me it. thank you.

  4. joobha on June 27th, 2008 15:03

    Hi. I can’t make it… I can’t manage an exception creating ntlmFilter bean… Could you show your xml files?

  5. FoX on June 27th, 2008 15:08

    Are you using Spring 2.0 namespace config?

  6. joobha on June 27th, 2008 15:16

    No. I’m using normal Spring config. My exceptions concern jcifs.smb…

  7. John Vance on June 28th, 2008 00:41

    Looks like you made a cut and paste error. The virtual filter chain snippet is actually just your FilterSecurityInterceptor entry. You need something that looks like this:

  8. John Vance on June 28th, 2008 00:43

    Well, I tried to paste in my filterChainProxy, but it got snipped out.

    Anyway, you need a filterChainProxy. I’d like to see which filters you use and in what order.

  9. joobha on June 29th, 2008 09:41

    Thanks for reply. Actually that’s my problem. What I need in web.xml?

  10. joobha on June 29th, 2008 10:44

    It’s me again..
    When I follow the instructions I’ve got: BeanCreationException: Error creating bean with name ‘httpSessionContextIntegrationFilter’ because of InvalidPropertyException: Invalid property ‘context’ of bean class [org.springframework.security.context.HttpSessionContextIntegrationFilter]: No property ‘context’ found.

    I’ve changed this property name to ‘contaxtClass’ and the initialization is completed. But…then i have: org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext

    What’s wrong now? Any ideas? Thanks a lot.

  11. FoX on June 29th, 2008 14:56

    You need to make sure that your authenticated windows user is also available in your userMap in the userDetailsService.

  12. joobha on June 29th, 2008 15:35

    yes, it is. I thought that it is problem with securityContext or..parameters given in ntlmFilter.

  13. joobha on July 1st, 2008 11:24

    Hi. I can’t make it working…In NtlmProcessingFilter.doFilter(..) in line 327 request.getHeader(“Authorization”) returns always null..

  14. Dani on July 8th, 2008 16:11

    I think is missing the filterChain, here is an example that work for me:

    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /**=httpSessionContextIntegrationFilter, exceptionTranslationFilter, ntlmFilter, filterSecurityInterceptor

    I’ve also have to add this file: jcifs-1.2.6.jar

  15. John Vance on July 8th, 2008 21:14

    You should look at the new security.xml syntax, which massively simplifies configuration. The filter chain and its order is implied.

    http://static.springframework.org/spring-security/site/reference/html/ns-config.html

  16. arek on July 31st, 2008 15:30

    I don’t know if you found a solution for your AuthenticationCredentialsNotFoundException problem. I met exactly the same issue and I found that just after invoking SecurityContextHolderAwareRequestFilter Authentication header vanishes. So I set servlet-api-provision parameter on bean to false. It works for me.

  17. arek on July 31st, 2008 15:38

    Last sentence is visible not exactly I wrote it. So again: I set servlet-api-provision parameter on “http” bean to false.