Announcement Announcement Module
Collapse
No announcement yet.
Can I mix authentication and authorities providers in the same app/context? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Can I mix authentication and authorities providers in the same app/context?

    Hello,

    I have a requirement for an application I'm building at the office.

    We are using Spring MVC 3.1/Tomcat6

    I've got my basic framework up and running properly, but I'm having an issue with authentication and authorities.

    Basically, what I want to do is:

    1) Login/Authenticate via LDAP
    2) Define a jdbc-user-service that will use one our database for authorities

    I've gotten to the point where #1 is working properly, but I can't figure out how to make Spring Security check the user-service outside of LDAP for authorities. In the log4j DEBUG level-logging, I don't see anything that implies it's looking at my jdbc-based authorities.

    Here is my (current) security config:

    Code:
    <?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xmlns:security="http://www.springframework.org/schema/security"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
         
        <security:jdbc-user-service id="userService" 
                     data-source-ref="billingDataSource" 
                     authorities-by-username-query="
                            SELECT   clp.login LOGIN_NM, 
                                     rights.display_name ROLE_NM 
                            FROM     ...
                            WHERE    ...
                              AND    clp.login = ?
        "/>
    
    
        <!-- Configure an authentication manager spring-security element to provide the authentication management and user search filter -->        
        <security:authentication-manager alias="authenticationManager" >
            <security:ldap-authentication-provider
                user-search-filter="(samaccountname={0})"/>
            <security:authentication-provider user-service-ref="userService" />
        </security:authentication-manager>    
        
        <security:http auto-config="true" use-expressions="true">
            <!-- IMPORTANT:  Intercept rules are applied top-to-bottom.  Order of precedence is CRITICAL -->
            <security:intercept-url pattern="/home" access="hasRole('Developer Rights')"/>
            <security:intercept-url pattern="/**" access="isAuthenticated()" />
        </security:http>    
    
        <!-- enable AOP security -->    
        <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled" />
        
        <!-- connect to ldap server to authenticate requests -->    
        <security:ldap-server url="ldap://ldapproxy.domain.com/dc=host,dc=domain" port="389"
                       manager-dn="cn=binder,dc=host,dc=domain"
                       manager-password="${ldap.reader.password}"/>
    
        
    </beans>

    Some log excerpts.
    Code:
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
    DEBUG [http-8080-4] (HttpSessionSecurityContextRepository.java:158) - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: '[email protected]231c1: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bf0231c1: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@40fe4ef1: Dn: cn=lastname\, firstname R,ou=Users,ou=Digital Corp,dc=blue,dc=host,dc=domain; Username: myuser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@380f4: RemoteIpAddress: 127.0.0.1; SessionId: 2A456EBAB78FADAD14EFFBAC1C677A84; Not granted any authorities'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 2 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 3 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 4 of 11 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
    DEBUG [http-8080-4] (AnonymousAuthenticationFilter.java:107) - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bf0231c1: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@40fe4ef1: Dn: cn=lastname\, firstname R,ou=Users,ou=Digital Corp,dc=blue,dc=host,dc=domain; Username: myuser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@380f4: RemoteIpAddress: 127.0.0.1; SessionId: 2A456EBAB78FADAD14EFFBAC1C677A84; Not granted any authorities'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
    DEBUG [http-8080-4] (FilterChainProxy.java:337) - /home at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
    DEBUG [http-8080-4] (AntPathRequestMatcher.java:103) - Checking match of request : '/home'; against '/home'
    DEBUG [http-8080-4] (AbstractSecurityInterceptor.java:194) - Secure object: FilterInvocation: URL: /home; Attributes: [hasRole('Billing Developer Rights')]
    DEBUG [http-8080-4] (AbstractSecurityInterceptor.java:310) - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bf0231c1: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@40fe4ef1: Dn: cn=lastname\, firstname R,ou=Users,ou=Digital Corp,dc=blue,dc=host,dc=domain; Username: myuser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@380f4: RemoteIpAddress: 127.0.0.1; SessionId: 2A456EBAB78FADAD14EFFBAC1C677A84; Not granted any authorities
    DEBUG [http-8080-4] (AffirmativeBased.java:65) - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@6c7d3bc4, returned: -1
    DEBUG [http-8080-4] (ExceptionTranslationFilter.java:172) - Access is denied (user is not anonymous); delegating to AccessDeniedHandler
    org.springframework.security.access.AccessDeniedException: Access is denied
    	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
    	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
    ...etc.

    Thanks in advance for any assistance you can provide.

  • #2
    Any ideas? Thanks again

    Comment


    • #3
      Please see the FAQ

      Comment


      • #4
        Rob, thanks for taking the time to respond.

        I did some more reading and what I did was:

        1) Implement the LdapAuthoritiesPopulator in my own class (rewriting it a bit from the example in the manual to be a little more 3.1.x friendly):

        Code:
        package ...
        import ...
        
        public class CustomAuthoritiesPopulator extends JdbcDaoSupport implements LdapAuthoritiesPopulator {
        
            private String authoritiesQuery;
            
            public List<SimpleGrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
                
                return getJdbcTemplate().query(authoritiesQuery,
                        new String[]{username},
                        new RowMapper<SimpleGrantedAuthority>() {
        
                            public SimpleGrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
                                return new SimpleGrantedAuthority(rs.getString(1));
                            }
                            
                        });
            
            }
        
            public String getAuthoritiesQuery() {
                return authoritiesQuery;
            }
        
            public void setAuthoritiesQuery(String authoritiesQuery) {
                this.authoritiesQuery = authoritiesQuery;
            }
            
            
        }

        2. Created an explicit bean for my custom class:

        Code:
            <bean id="customAuthoritiesPopulator" class="com.code.my.CustomAuthoritiesPopulator">
                <property name="dataSource" ref="billingDataSource" />
                <property name="authoritiesQuery" value="            
                                SELECT   clp.login LOGIN_NM, 
                                         TRIM('ROLE_' || UPPER(REPLACE(rights.display_name, ' ', '_'))) ROLE_NM 
                                  ...etc...
                                  AND    clp.login = ?"/>
            </bean>
        3. Created an explicit bean of class org.springframework.security.ldap.authentication.L dapAuthenticationProvider in my security xml file using my custom class for authorities as noted in the second constructor arg (i left out the contextSource and userSearch definitions for brevity's sake):

        Code:
            <bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
                <constructor-arg>
                    <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
                        <constructor-arg ref="contextSource"/>
                        <property name="userSearch" ref="userSearch" />
                        <property name="userDnPatterns">
                            <list><value>uid={0},ou=people</value></list>
                        </property>
                    </bean>
                </constructor-arg>
                <constructor-arg ref="customAuthoritiesPopulator"/>
            </bean>

        3) Changed the authentication-manager to reference my explicit bean created in step #2 above

        Code:
             <security:authentication-manager alias="authenticationManager">
                <security:authentication-provider ref="ldapAuthProvider"/>
            </security:authentication-manager>

        The problem I'm having now is that (after being presented with the login screen, and hitting the submit button after filling out my credentials), I'm getting a 404 where my controller used to appear.

        I must be missing another piece of the puzzle here, can you point me in the right direction?

        Outside of any explicit bean configurations in my security xml file, I'm using these security xml namespace configurations:

        Code:
            <security:authentication-manager alias="authenticationManager">
                <security:authentication-provider ref="ldapAuthProvider"/>
            </security:authentication-manager>
        
            <security:http auto-config="true" use-expressions="true">
                <security:intercept-url pattern="/j_spring_security_check" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
                <security:intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
                <security:intercept-url pattern="/**" access="isAuthenticated()" />
                <security:intercept-url pattern="/**" access="permitAll"/>
            </security:http>    
            
            <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled" authentication-manager-ref="ldapAuthProvider"/>
        Do i need to abandon security namespace configuration altogether in favor of explicit bean configuration?


        Thanks again for your help. I tried to be brief but also provide as much detail as needed.. hard line to walk..

        Comment


        • #5
          If you disable Spring Security are you able to navigate to the controller? If so, it may just be a problem with the controller. Do you have secured annotations on your Controller? If you remove the annotations or the global-method-security does it fix the issue? If so, you probably need to specify proxy-target-class="true" on the global-method-security element.

          PS: What URL is being requested, what do the logs look like, do any other URLs work?

          Comment


          • #6
            Rob, thanks again for taking the time.

            I think what was happening was that I had some comments in my SQL that dictates the authorities for user(s) which shouldn't have been in there (works fine in my sql IDE obviously because it's a multiline statement, but it was being processed as a single-line statement by Spring, so, who the comments were throwing something off. Dumb/rookie move on my part.

            I've got it all working properly now.. it's authenticating off of LDAP and pulling authorities out of our DB. Perfect.

            Thanks again.

            Comment

            Working...
            X