Home

NTLM with Spring Security 2.0

Thursday, June 05th, 2008 | Author: FoX

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>
Tags »

Trackback: Trackback-URL | Comments Feed: RSS 2.0
Category: Spring

Comments and pings are currently closed.

17 Responses

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

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

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

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

  5. Are you using Spring 2.0 namespace config?

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

  7. 7
    John Vance 
    Saturday, 28. June 2008

    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. 8
    John Vance 
    Saturday, 28. June 2008

    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. Thanks for reply. Actually that’s my problem. What I need in web.xml?

  10. 10
    joobha 
    Sunday, 29. June 2008

    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. You need to make sure that your authenticated windows user is also available in your userMap in the userDetailsService.

  12. 12
    joobha 
    Sunday, 29. June 2008

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

  13. 13
    joobha 
    Tuesday, 1. July 2008

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

  14. 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. 15
    John Vance 
    Tuesday, 8. July 2008

    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. 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. Last sentence is visible not exactly I wrote it. So again: I set servlet-api-provision parameter on “http” bean to false.