Role-Based Access Control (RBAC) is a security paradigm used to restrict system access to authorized users based on their roles. In RBAC, permissions are assigned to specific roles rather than to individual users. This model simplifies management as roles can be easily modified without affecting user permissions directly.
In this tutorial, we'll explore how to implement RBAC in a Spring Boot application with JWT authentication. We'll cover:
Create a Spring Boot Project: Use Spring Initializr to generate a new project. Include the following dependencies:
Project Structure: Organize your project as follows:
src/main/java/com/basicutils/rbac ├── config ├── controller ├── model ├── repository ├── security └── service
Create a Role class representing user roles.
package com.basicutils.rbac.model; import jakarta.persistence.*; import java.util.HashSet; import java.util.Set; @Entity @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String name; @ManyToMany(mappedBy = "roles") private Set<User> users = new HashSet<>(); // Getters and setters }
Create a User class representing the application users.
package com.basicutils.rbac.model; import jakarta.persistence.*; import java.util.HashSet; import java.util.Set; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); // Getters and setters }
Security Configuration: Configure Spring Security to handle RBAC.
package com.basicutils.rbac.config; import com.basicutils.rbac.filter.JwtRequestFilter; import com.basicutils.rbac.service.UserDetailsServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // Using BCrypt for password encoding } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() // Allow access to auth endpoints .anyRequest().authenticated() // Secure all other endpoints ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // Use stateless session http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // Add JWT filter return http.build(); } @Bean public AuthenticationManager authManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); return authenticationManagerBuilder.build(); } }
Role-Based Authorization: Configure the security filter chain to secure specific endpoints based on user roles.
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/admin/**").hasRole("ADMIN") // Only ADMIN can access these endpoints .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN") // USER and ADMIN can access these .requestMatchers("/api/auth/**").permitAll() // Allow access to auth endpoints .anyRequest().authenticated() // Secure all other endpoints ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }
Use Postman to test the implementation of RBAC:
/api/auth/register
/api/auth/login
/api/admin/resource
/api/user/resource
Implementing Role-Based Access Control (RBAC) in Spring Boot enhances application security by enforcing strict access controls based on user roles. By following the steps outlined in this tutorial, you can effectively manage user permissions and ensure that sensitive resources are adequately protected.
simplify and inspire technology
©2024, basicutils.com