Spring Security (series) - Authorities and Roles #6

Now that we know who you are, let's see what you CAN do 🧐 !

Brief

So we got through with the Authentication flow where we identify the user and we saw that there are more than a few ways to do that (database, third-party) . We move into the Authorization part of Security.

Implementation

Authorization is the process where we check the permissions of a user to do certain actions.

Spring Security provides us two main concepts by which we define permissions:

  • Authority - action-based permissions (ex: canReadTransactions, canDeleteUser)
  • Role - role-based permissions (ex: Admin, Client, BackOffice)

Practically, there is no conceptual difference between them. A role is only an authority prefixed with 'ROLE_'

public UserBuilder roles(String... roles) {
    List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
    for (String role : roles) {
    	Assert.isTrue(!role.startsWith("ROLE_"),
				() -> role + " cannot start with ROLE_ (it is automatically added)");
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
			}
    return authorities(authorities);
}
User.java 

Continuing on the footsteps of the database login example, let's create two endpoints that we will secure by only letting some users access them:

  • /admin - can only be access by users with ADMIN role
  • /client - can only be access by users with CLIENT role
@RestController
public class Controller {

    @GetMapping("/admin")
    public String admin() {
        return "Admin";
    }

    @GetMapping("/client")
    public String client() {
        return "Client";
    }
}
Controller.java

And for the security configuration, we only need to override the configure(HttpSecurity http) of the WebSecurityConfigurerAdapter and use the mvcMatchers to match URLs with roles.

@Configuration
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

---

@Override    
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic();
    http.authorizeRequests().mvcMatchers("/admin").hasRole("ADMIN");
    http.authorizeRequests().mvcMatchers("/client").hasRole("CLIENT");
    }
}
SecurityConfig.java

Actually defining the roles for some users will require us creating 2 new tables:

  • role - where we define the roles in our application
  • user_x_role - where we match roles with users.

We need to define the Role entity matching the role table.

@Entity
@Table(name = "role", schema = "public")
@Getter
@Setter
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @Column(name = "role")
    private String role;
}
Role.java

And then, the roles need to be added to the User entity.

@Entity
@Table(name = "user", schema = "public")
@Getter
@Setter
public class User {
    ---
    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    }, fetch = FetchType.EAGER)
    @JoinTable(name = "user_x_role",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
}
User.java

(I know that fetch = FetchType.EAGER is not the best thing to do in terms of performance, but for this example, it will do the trick)

The last link in the chain is to add the roles to the Spring Security UserDetails object that gets added in the Security Context on a successful authentication.

public class CustomUserDetails implements UserDetails {
   ---
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getRole()))
                .collect(Collectors.toList());
    }
   ---
}
CustomUserDetails.java

Testing it with Postman:

As always, you can find the full code here: https://github.com/andreiszu/spring-security/tree/authorities-roles


💡
Don't miss out on more posts like this! Susbcribe to our free newsletter!
💡
Currently I am working on a Java Interview e-book designed to successfully get you through any Java technical interview you may take.
Stay tuned! 🚀