Implementar roles y permisos en una aplicación Spring Boot es una tarea común cuando se necesita controlar el acceso a diferentes partes de la aplicación. A continuación, te guiaré paso a paso para configurar roles y permisos en Spring Boot utilizando Spring Security.
Primero, asegúrate de tener las dependencias necesarias en tu archivo pom.xml
:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA (si usas una base de datos) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Base de datos H2 (o cualquier otra base de datos que prefieras) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Otras dependencias que necesites -->
</dependencies>
Crea una clase de configuración para Spring Security. Esta clase definirá cómo se manejarán los roles y permisos.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll() // Rutas públicas
.antMatchers("/admin/**").hasRole("ADMIN") // Solo accesible por ADMIN
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // Accesible por USER y ADMIN
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") // Página de login personalizada
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
En el ejemplo anterior, hemos definido dos roles: USER
y ADMIN
. Los permisos se asignan en función de los roles:
/public/**
: Accesible por todos (sin autenticación).
/admin/**
: Solo accesible por usuarios con el rol ADMIN
.
/user/**
: Accesible por usuarios con los roles USER
o ADMIN
.
Crea controladores para manejar las rutas protegidas:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/dashboard")
public String adminDashboard() {
return "Admin Dashboard";
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/profile")
public String userProfile() {
return "User Profile";
}
}
@RestController
@RequestMapping("/public")
public class PublicController {
@GetMapping("/info")
public String publicInfo() {
return "Public Information";
}
}
Si deseas almacenar usuarios, roles y permisos en una base de datos, puedes configurar Spring Data JPA para manejar estas entidades.
import javax.persistence.*;
import java.util.Set;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
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;
// Getters y Setters
}
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Set<User> users;
// Getters y Setters
}
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getRoles()
);
}
}
Modifica la clase SecurityConfig
para usar el CustomUserDetailsService
:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ahora puedes probar tu aplicación:
/public/info
sin autenticación.
/user/profile
o /admin/dashboard
sin autenticarte, deberías ser redirigido al login.
user/password
o admin/admin
) y verifica que los roles y permisos funcionen correctamente.
@PreAuthorize
).
Con estos pasos, deberías tener una configuración básica de roles y permisos en tu aplicación Spring Boot.
Jorge García
Fullstack developer