Volver a la página principal
lunes 27 enero 2025
27

Cómo implementar roles y permisos en una aplicación Spring Boot

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.

1. Dependencias necesarias

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>

2. Configuración de Spring Security

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);
    }
}

3. Definición de Roles y Permisos

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.

4. Controladores para Rutas Protegidas

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";
    }
}

5. Configuración de la Base de Datos (Opcional)

Si deseas almacenar usuarios, roles y permisos en una base de datos, puedes configurar Spring Data JPA para manejar estas entidades.

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
}

Repositorios:

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);
}

Servicio de Detalles de Usuario:

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()
        );
    }
}

6. Configuración de Spring Security con Base de Datos

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();
    }
}

7. Pruebas

Ahora puedes probar tu aplicación:

  • Accede a /public/info sin autenticación.
  • Intenta acceder a /user/profile o /admin/dashboard sin autenticarte, deberías ser redirigido al login.
  • Inicia sesión con los usuarios que configuraste (user/password o admin/admin) y verifica que los roles y permisos funcionen correctamente.

8. Consideraciones Adicionales

  • Encriptación de Contraseñas: Siempre usa un algoritmo de encriptación fuerte como BCrypt para almacenar contraseñas.
  • Gestión de Roles y Permisos: Si tu aplicación crece, considera usar una gestión más granular de permisos (por ejemplo, con @PreAuthorize).
  • Seguridad en Producción: Asegúrate de configurar correctamente HTTPS, CSRF, y otras medidas de seguridad en un entorno de producción.

Con estos pasos, deberías tener una configuración básica de roles y permisos en tu aplicación Spring Boot.

Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer