Loading Roles from the Database

User and Role Tables

In ShopEase the roles live in the database. A user can have several roles (for example a seller who also shops). The entity mapping is covered in the Hibernate & JPA material; here we only need the result.

@Entity
@Table(name = "users")
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String email;

    private String passwordHash;
    private boolean enabled = true;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
    @Enumerated(EnumType.STRING)
    @Column(name = "role")
    private Set<Role> roles = new HashSet<>();

    // getters...
}

public enum Role { CUSTOMER, SELLER, ADMIN }

UserDetailsService with Roles

@Service
public class ShopUserDetailsService implements UserDetailsService {

    private final UserRepository users;

    public ShopUserDetailsService(UserRepository users) {
        this.users = users;
    }

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User u = users.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("No user " + email));

        List<GrantedAuthority> authorities = u.getRoles().stream()
                .map(r -> (GrantedAuthority) new SimpleGrantedAuthority("ROLE_" + r.name()))
                .toList();

        return org.springframework.security.core.userdetails.User.withUsername(u.getEmail())
                .password(u.getPasswordHash())
                .authorities(authorities)
                .disabled(!u.isEnabled())
                .build();
    }
}

Reading the Current User's Authorities

@GetMapping("/api/me")
public Map<String, Object> me(Authentication authentication) {
    return Map.of(
        "email", authentication.getName(),
        "authorities", authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority).sorted().toList());
}

Example

curl -u [email protected]:ravi123 localhost:8080/api/me

Example output

HTTP/1.1 200 OK
{"email":"[email protected]","authorities":["ROLE_CUSTOMER","ROLE_SELLER"]}

Exercise

Exercise: Write URL rules so that: GET /api/reports/** needs the authority report:read, POST /api/coupons needs role ADMIN, and everything under /api/cart/** needs any logged-in user.

Show answer
http.authorizeHttpRequests(auth -> auth
    .requestMatchers(HttpMethod.GET, "/api/reports/**").hasAuthority("report:read")
    .requestMatchers(HttpMethod.POST, "/api/coupons").hasRole("ADMIN")
    .requestMatchers("/api/cart/**").authenticated()
    .anyRequest().authenticated());