Search code examples
springspring-mvcauthenticationspring-securityspring-annotations

Spring security jdbc config to auth user with multiple roles


I am currently working on a java based invoice app. What I need is , to auth a user with multiple Roles, e.g Peter should have role ROLE_SETTING,ROLE_REPORT,ROLE_QUOTE, while Anne only has one role ROLE_SETTING etc...

Here is my spring-security-config.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"  
 xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
 http://www.springframework.org/schema/security  
 http://www.springframework.org/schema/security/spring-security-3.2.xsd">  

 <http auto-config="true">  
  <access-denied-handler error-page="/403page" />  
  <intercept-url pattern="/makebill*" access="ROLE_ADMIN,ROLE_MAKEBILL" />
  <intercept-url pattern="/report*" access="ROLE_ADMIN,ROLE_REPORT" />
  <intercept-url pattern="/stock*" access="ROLE_ADMIN,ROLE_STOCK" />
  <intercept-url pattern="/customer*" access="ROLE_ADMIN,ROLE_CUSTOMER" />
  <!-- <intercept-url pattern="/setting*" access="ROLE_ADMIN,ROLE_SETTING" />   -->
  <intercept-url pattern="/mainmenu*" access="ROLE_ADMIN,ROLE_MAKEBILL,ROLE_SETTING,ROLE_CUSTOMER,ROLE_REPORT,ROLE_STOCK" />  
  <form-login 
    login-page='/login' username-parameter="username"  
   password-parameter="password" default-target-url="/mainmenu"  
   authentication-failure-url="/login?authfailed" />  
  <logout logout-success-url="/login?logout" />  
 </http>  

 <authentication-manager> 
     <authentication-provider>  
        <jdbc-user-service 
            data-source-ref="dataSource"  
            users-by-username-query="select username,password, enabled from person where username=?"  
            authorities-by-username-query="select username, role from person, role where username =?  " />  
     </authentication-provider> 
 </authentication-manager> 


</beans:beans>  

the authentication-manager currently working fine with just one role, what i mean is...when the sql query authorities-by-username-query="select username, role from person, role where username =? " runs, if the returned value is just one role, e.g ROLE_REPORT, the application works fine, but if have a record in db like, ROLE_REPORT,ROLE_SETTING,ROLE_CUSTOMER , when the query retrieves this value, the application will return me 404 error, it seems I won't be able to grant a user with multiple roles.

Can anyone please point me out what I have done wrong. Thanks.


Solution

  • I found a solution myself, implemented my own UserDetails, here are the code

    @Service("assembler")
    public class Assembler {
    
      @Transactional(readOnly = true)
      User buildUserFromUserEntity(Person person) {
    
        String username = person.getUsername();
        String password = person.getPassword();
        boolean enabled = true;
    
        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        String[] authStrings = person.getRole().split(",");
        for(String authString : authStrings) {
            System.out.println("all auth roles: " + authString);
            authorities.add(new SimpleGrantedAuthority(authString));
        }
    
    
        User user = new User(username, password, enabled,
          true, true, true, authorities);
    
        return user;
      }
    }
    
    
    
    
    
    
    @Service("userDetailsService") 
    public class UserDetailsServiceImpl implements UserDetailsService {
    
      @Autowired private PersonService personService;
      @Autowired private Assembler assembler;
    
      @Transactional(readOnly = true)
      public UserDetails loadUserByUsername(String username)
          throws UsernameNotFoundException, DataAccessException {
    
        UserDetails userDetails = null;
        Person person = null;
    
        List<Person> listPeople = personService.listPeople();
        for (Person p: listPeople) {
            if (p.getUsername().equals(username)) {
                person = p;
            }
        }
    
        if (person == null)
          throw new UsernameNotFoundException("user not found");
    
        return assembler.buildUserFromUserEntity(person);
      }
    }
    

    and here is the spring-security-config.xml

    <http auto-config="true">  
      <access-denied-handler error-page="/403page" />  
      <intercept-url pattern="/makebill*" access="ROLE_ADMIN,ROLE_MAKEBILL" />
      <intercept-url pattern="/report*" access="ROLE_ADMIN,ROLE_REPORT" />
      <intercept-url pattern="/stock*" access="ROLE_ADMIN,ROLE_STOCK" />
      <intercept-url pattern="/customer*" access="ROLE_ADMIN,ROLE_CUSTOMER" />
      <intercept-url pattern="/setting*" access="ROLE_ADMIN,ROLE_SETTING" />
      <intercept-url pattern="/mainmenu*" access="ROLE_ADMIN,ROLE_MAKEBILL,ROLE_SETTING,ROLE_CUSTOMER,ROLE_REPORT,ROLE_STOCK" />  
      <form-login 
        login-page='/login' username-parameter="username"  
       password-parameter="password" default-target-url="/mainmenu"  
       authentication-failure-url="/login?authfailed" />  
      <logout logout-success-url="/login?logout" />  
     </http>  
    
    
     <beans:bean id="daoAuthenticationProvider"
     class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
      <beans:property name="userDetailsService" ref="userDetailsService"/>
    </beans:bean>
    
    <beans:bean id="authenticationManager"
        class="org.springframework.security.authentication.ProviderManager">
      <beans:property name="providers">
        <beans:list>
          <beans:ref local="daoAuthenticationProvider" />
        </beans:list>
      </beans:property>
    </beans:bean>
    
    <authentication-manager>
      <authentication-provider user-service-ref="userDetailsService">
    
      </authentication-provider>
    </authentication-manager>
    

    I am now able to assign multiple roles to a single user perfectly. Thanks for reading :)