Multiple users with Spring Security

I have 4 different type of users. Every type have own role and additional attributes. User is parent, and 3 inheritors.

Also I use Spring Data.

In which way I can implement UserDetailsService to use 4 different types of users?

Now I have:

@Inheritance(strategy = InheritanceType.JOINED)
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String password;
    private String email;
    @ManyToMany
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

}

And

public class Employee extends User implements Serializable {

    private static final long serialVersionUID = 1L;


    private String fullName;
    @ManyToMany(mappedBy = "employees")
    private Set<Project> projects;
    @OneToMany(mappedBy = "employee")
    private Set<Task> tasks;

}

And other.

Upvotes: 2

Views: 9950

Answers (1)

Klaus Groenbaek
Klaus Groenbaek

Reputation: 5035

Since you are talking about UserDetailsService I assume that you use Spring Security. If you only need to authenticate/authorize users, I'm not sure you need the full user management the UserDetailsService offers. It may be enough to define a single AuthenticationProvider and to the query here

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new AuthenticationProvider() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                // Do you database query here
                ArrayList<GrantedAuthority> authorities = new ArrayList<>();
                authorities.add(new SimpleGrantedAuthority("ROLE_"));  // list of roles from database 
                return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(),
                           authentication.getCredentials(), authorities);
            }

            @Override
            public boolean supports(Class<?> authentication) {
                return true;
            }
        })
    }
}

This example is inline and you should probably make the AuthenticationProvider a real class.

The AuthenticationProvider is called with an unauthenticated Authentication, which has been created by a filter, typically BasicAuthenticationFilter or UsernamePasswordAuthenticationFilter. After this, the Authentication is given to the ProviderManager which ask each of the AuthenticationProvider if they can authenticate this type of Authentication (this is what the supports() method is for). Once a suitable AuthenticationProvider is found, it is asked to authenticate — This is where you do your database lookup and find the roles from the database, and construct a new Authentication with the list of GrantedAuthorities based on the roles from the database.

Be aware that you should put "ROLE_" in front of the roles (unless you store them like that), otherwise it will not work with the declarative access configured using HttpSecurity

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
         .antMatchers("/","/home").permitAll()
         .antMatchers("/admin/**").access("hasRole('ADMIN')")
         // more lines 
}

Here ADMIN maps to the GrantedAuthority ROLE_ADMIN.

Upvotes: 1

Related Questions