Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

...

To do this ensure that the following two lines appear in logging.properties and any others referencing security are removed (in case they reduce the logging)

Code Block
nonenone
titleEnable security process logging
linenumberstrue
none
log4j.logger.org.acegisecurity=DEBUG
log4j.logger.com.cohga.server.security=DEBUG

...

Looking at the default security.xml file it contains the following near the top:

Code Block
xmlxml
titleDefault request filter chain
linenumberstrue
xml
	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/server/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
				/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
			</value>
		</property>
	</bean>

...

That should be the final new section we need to add, since the sections that it references should already exist. So all that remains is to add the first section we added to the list of filters:

Code Block
xmlxml
titleAdding NTLM filter to request filter chain
linenumberstrue
xml
	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/server/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
				/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
			</value>
		</property>
	</bean>

...

To do this we add a new section to the security.xml file to define a new NtlmProcessingFilter

xml
Code Block
xml
titleThe root NTLM filter definition
linenumberstrue
xml
	<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilter">
		<property name="defaultDomain"><value>DOMAINNAME</value></property>
		<property name="domainController"><value>172.16.0.30</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
	</bean>

...

The ntlmEntryPoint takes care of the communication for the server to obtain the Windows userid from the browser.
The ntlmAuthenticationManager then sends that information through a list of AuthenticationProviders to validate that it's correct.

Code Block
xmlxml
titleNTLM entry point for obtaining the user information from browser
linenumberstrue
xml
	<bean id="ntlmEntryPoint" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilterEntryPoint"/>
xml
Code Block
xml
titleNTLM authentication manager for verifying the user information from browser
linenumberstrue
xml
	<bean id="ntlmAuthenticationManager" class="org.acegisecurity.providers.ProviderManager">
		<property name="providers">
			<list>
				<ref local="smbAuthenticationProvider"/>
				<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
					<property name="key" value="changeThis"/>
				</bean>
				<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
					<property name="key" value="changeThis"/>
				</bean>
			</list>
		</property>
	</bean>

...

Setting up the smbAuthenticationProvider is just a matter of configuring the SmbNtlmAuthenticationProvider with the authorizationProvider provider to be used.

xml
Code Block
xml
titleA SMB NTLM aware authentication provider
linenumberstrue
xml
	<bean id="smbAuthenticationProvider" class="org.acegisecurity.providers.smb.SmbNtlmAuthenticationProvider">
		<property name="authorizationProvider">
			<ref local="nullDaoAuthenticationProvider"/>
		</property>
	</bean>

In this case we're referencing yet another item, the nullDaoAuthenticationProvider authentication provider.
The nullDaoAuthenticationProvider is a simple authentication provider that uses a separate UserDetailsService to retrieve the information about what roles a user has, and if you're using the default security.xml file for this that will be the users.properties file.
Alternatively the UserDetailsService could be accessing a database to retrieve the users roles, and later we'll be looking at changing this to use Active Directory (via LDAP) to determine the users roles.

Code Block
xmlxml
titleA SMB simple password authenticator
linenumberstrue
xml
	<bean id="nullDaoAuthenticationProvider" class="org.acegisecurity.providers.smb.NullPasswordDaoAuthenticationProvider">
		<property name="userDetailsService" ref="userDetailsService"/>
		<property name="userCache">
			<bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
				<property name="cache">
					<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
						<property name="cacheManager">
							<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
						</property>
						<property name="cacheName" value="userCache"/>
					</bean>
				</property>
			</bean>
		</property>
	</bean>

...

Depending upon the version of active directory you're running you may need to specify a username/password for the ntlmProcessingFilter, so if you find authentication errors in the weave.log file after enabling integrated authentication then change the ntlmProcessingFilter to the following and set the appropriate username/password.

xml
Code Block
xml
titleSetting username/password for domain access
linenumberstrue
xml
	<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.NtlmProcessingFilter">
		<property name="defaultDomain"><value>DOMAINNAME</value></property>
		<property name="domainController"><value>172.16.0.30</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name = "JCifsProperties">
			<map>
				<entry key="jcifs.smb.client.username">
					<value>username</value>
				</entry>
				<entry key="jcifs.smb.client.password">
					<value>password</value>
				</entry>
			</map>
		</property>
	</bean>

...

The new configuration items that the IPFilteredNtlmProcessingFilter provides are excludedIpAddresses and includedIpAddresses, and are set as a list of IP addresses or address ranges.

xml
Code Block
xml
titleSelectively applying NTLM authentication
linenumberstrue
xml
	<bean id="ntlmProcessingFilter" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
		<property name="defaultDomain"><value>DOMAINNAME</value></property>
		<property name="domainController"><value>172.16.0.30</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name="excludedIpAddresses">
			<list>
				<value>192.168.2.0/24</value>
				<value>138.19.19.50</value>
			</list>
		</property>
		<property name="includedIpAddresses">
			<list>
				<value>172.16.0.0/16</value>
			</list>
		</property>
	</bean>

...

As of version 1.3.4 of the org.acegisecurity.ntlm bundle there's an additional property that can be set for the IPFilteredNtlmProcessingFilter, and that's defaultRole, which when set will add the role (exactly as it appears in the security.xml file) to the list of roles the user has. This allows you to utilise multiple Active Directory domain to authenticate user and provide access control based on what domain the user was authenticated against.
Note: If you're using LDAP to provide the users roles then it's also possible to set a defaultRole in the LDAP populator.

Code Block
xmlxml
titleUsing multiple domain for authentication
linenumberstrue
xml
	<bean id="ntlmProcessingFilterInternal" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
		<property name="defaultDomain"><value>INTERNAL</value></property>
		<property name="domainController"><value>172.16.0.30</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name="includedIpAddresses">
			<list>
				<value>172.16.0.0/16</value>
			</list>
		</property>
		<property name="defaultRole"><value>ROLE_INTERNAL</value></property>
	</bean>

	<bean id="ntlmProcessingFilterExternal" class="org.acegisecurity.ui.ntlm.IPFilteredNtlmProcessingFilter">
		<property name="defaultDomain"><value>EXTERNAL</value></property>
		<property name="domainController"><value>201.20.109.76</value></property>
		<property name="authenticationEntryPoint" ref="ntlmEntryPoint"/>
		<property name="authenticationManager" ref="ntlmAuthenticationManager"/>
		<property name="includedIpAddresses">
			<list>
				<value>201.20.0.0/16</value>
			</list>
		</property>
		<property name="defaultRole"><value>ROLE_EXTERNAL</value></property>
	</bean>

Not that to enable this both filters need to be added to the filter chain:

xml
Code Block
xml
titleAdding NTLM filter to request filter chain
linenumberstrue
xml
	<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
				PATTERN_TYPE_APACHE_ANT
				/server/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter1,ntlmProcessingFilter2,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterInvocationInterceptor
				/**=httpSessionContextIntegrationFilter,ntlmProcessingFilter1,ntlmProcessingFilter2,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
			</value>
		</property>
	</bean>

...

So we fist need to change the ntlmAuthenticationManager to

Code Block
xmlxml
titleEnabling LDAP support for authenticating users
linenumberstrue
xml
	<bean id="ntlmAuthenticationManager" class="org.acegisecurity.providers.ProviderManager">
		<property name="providers">
			<list>
				<ref local="ldapAuthenticationProvider"/>
				<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
					<property name="key" value="changeThis"/>
				</bean>
				<bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
					<property name="key" value="changeThis"/>
				</bean>
			</list>
		</property>
	</bean>

And then setup the new ldapAuthenticationProvider as follows:

Code Block
xmlxml
titleLDAP authentication provider that can handle NTLM authenticated users
linenumberstrue
xml
	<bean id="ldapAuthenticationProvider" class="org.acegisecurity.ui.ntlm.ldap.authenticator.NtlmAwareLdapAuthenticationProvider">
		<constructor-arg>
			<ref local="authenticatorLdap"/>
		</constructor-arg>
		<constructor-arg>
			<ref local="populatorLdap"/>
		</constructor-arg>
	</bean>

...

The authentication would be configured as follows:

xml
Code Block
xml
titleThe authenticator that will search an LDAP directory for the user
linenumberstrue
xml
	<bean id="authenticatorLdap" class="org.acegisecurity.ui.ntlm.ldap.authenticator.NtlmAwareLdapAuthenticatorImpl">
		<constructor-arg>
			<ref local="initialDirContextFactory"/>
		</constructor-arg>
		<property name="userSearch">
			<ref local="userSearchLdap"/>
		</property>
	</bean>

...

The initialDirContextFactory is also used by the userSearchLdap and the populaterLdap beans so we'll look at that first

Code Block
xmlxml
titleSetting up a connection to the LDAP server
linenumberstrue
xml
	<bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
		<constructor-arg value="ldap://192.168.0.16:389/"/>
		<property name="managerDn">
			<value>CN=Administrator,OU=Users,DC=cohga,DC=local</value>
		</property>
		<property name="managerPassword">
			<value>password</value>
		</property>
	</bean>

...

The two final beans, the userSearchLdap and populatorLdap also require information that is specific to the environment you're running within, the userSearchLdap beans would be something like the following:

xml
Code Block
xml
titleSetting up an LDAP search for a user
linenumberstrue
xml
	<bean id="userSearchLdap" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
		<constructor-arg>
			<value>OU=Users,DC=cohga,DC=local</value>
		</constructor-arg>
		<constructor-arg>
			<value>(sAMAccountName={0})</value>
		</constructor-arg>
		<constructor-arg>
			<ref local="initialDirContextFactory" />
		</constructor-arg>
		<property name="searchSubtree">
			<value>true</value>
		</property>
	</bean>

...

Finally the populatorLdap is responsible for mapping the username to the roles and would be configured as follows

xml
Code Block
xml
titleSetting up an LDAP serach for groups
linenumberstrue
xml
	<bean id="populatorLdap" class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
		<constructor-arg>
			<ref local="initialDirContextFactory"/>
		</constructor-arg>
		<constructor-arg>
			<value>OU=Weave,DC=cohga,DC=local</value>
		</constructor-arg>
		<property name="groupRoleAttribute">
			<value>cn</value>
		</property>
		<property name="searchSubtree">
			<value>true</value>
		</property>
		<property name="rolePrefix">
			<value>ROLE_</value>
		</property>
		<property name="convertToUpperCase">
			<value>true</value>
		</property>
		<property name="groupSearchFilter">
			<value>(member={0})</value>
		</property>
		<property name="defaultRole">
			<value>ROLE_USERS</value>
		</property>
	</bean>

...

To implement the new authorities populator you should change:

Code Block
xmlxml
titleOld DefaultLdapAuthoritiesPopulator
linenumberstrue
xml
<bean id="populator" class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
	...
</bean>

to

xml
Code Block
xml
titleNew SupplementedLdapAuthoritiesPopulator with reference to user details service
linenumberstrue
xml
<bean id="populator" class="org.acegisecurity.providers.ldap.populator.SupplementedLdapAuthoritiesPopulator">
	<property name="userDetailsService" ref="ldapDetailsService"/>
	...
</bean>

This will change it from DefaultLdapAuthoritiesPopulator to SupplementedLdapAuthoritiesPopulator and add a reference to the service that will provide the additional role information, which can be included by adding:

xml
Code Block
xml
titleNew user details service reading roles from a file
linenumberstrue
xml
<bean id="ldapDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
	<property name="userProperties">
		<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
			<property name="location" value="ldap.properties"/>
		</bean>
	</property>
</bean>

...

Alternatively you could also use the following if you just had a couple of users:

Code Block
xmlxml
titleNew user details service reading roles from its configuration
linenumberstrue
xml
<bean id="ldapDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
	<property name="userMap">
		<value>
			shaun=password,ROLE_TEST
		</value>
	</property>
</bean>

...