es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Verificar el rol de usuario en los endpoints autogenerados de JpaRepository.

Supongamos que tengo un JpaRepository que gestiona una entidad y proporciona puntos finales CRUD auto-generados para la creación y lectura de dicha entidad:

@CrossOrigin
@RepositoryRestResource(path = "testentity", itemResourceRel = "testentity", collectionResourceRel = "testentity")
public interface TestEntityRepository extends JpaRepository<testentity, long=""> {

}

Dado que esto proporciona resultados paginables y ordenables, me gustaría usar los puntos finales proporcionados, pero quiero restringir el acceso a estos según los roles de usuario. Los usuarios se almacenan en el campo “session” de las solicitudes respectivas enviadas por el cliente:

request.getSession().getAttribute("user")

Cada usuario tiene un conjunto de roles que especifican sus privilegios (solo una lista de cadenas). ¿Existe una manera fácil de mantener los puntos finales auto-generados proporcionados por JpaRepository y restringir el acceso a ellos según el contenido del campo user.roles?

Tenga en cuenta que los usuarios se generan después de autenticarse previamente a través de una aplicación de terceros. Esto significa que los roles de cada usuario no se persisten en una fuente de datos legible, sino que deben solicitarse a dicha aplicación cada vez que un usuario inicia sesión.

El proceso de inicio de sesión se ve así:

  • El usuario envía el nombre de usuario y la contraseña.
  • La aplicación envía el nombre de usuario y la contraseña a la aplicación de terceros.
  • La aplicación de terceros envía de vuelta si el usuario existe y sus roles.
  • La aplicación crea un objeto User con los roles recibidos.
  • El usuario se utiliza en la sesión y se descarta cuando la sesión caduca (esto significa que cada usuario solo existe durante la duración de cada sesión).

Clase User:

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

    @ElementCollection
    private List<string> roles; 
    private String token;
    private DateTime expiration;
}

Para aclarar: si hay 2 roles (‘user’, ‘admin’), quiero que los puntos finales POST solo sean accesibles para los usuarios con el rol ‘admin’, mientras que los métodos GET deben ser accesibles para cualquier usuario (tanto ‘admin’ como ‘user’).</testentity,>

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, hay una forma de mantener los endpoints autogenerados proporcionados por JpaRepository y restringir el acceso a ellos según el contenido del campo user.roles. Una forma de lograr esto es utilizando Spring Security.

    En primer lugar, debes configurar Spring Security en tu aplicación. Esto se puede hacer incluyendo la dependencia de Spring Security en tu archivo pom.xml o build.gradle y creando una clase de configuración de seguridad que extienda WebSecurityConfigurerAdapter. En esta clase de configuración de seguridad, puedes definir las reglas de acceso para diferentes endpoints basados en los roles del usuario.

    Por ejemplo:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserRepository userRepository;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers(HttpMethod.POST, "/testentity").hasRole("ADMIN")
                    .anyRequest().authenticated()
                    .and().httpBasic()
                    .and().csrf().disable();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(username -> {
                User user = userRepository.findByUsername(username);
                if (user != null) {
                    return new org.springframework.security.core.userdetails.User(
                            user.getUsername(),
                            user.getPassword(),
                            AuthorityUtils.createAuthorityList(user.getRoles().toArray(new String[0])));
                } else {
                    throw new UsernameNotFoundException("Usuario/contraseña inválida");
                }
            });
        }
    }
    

    En la configuración anterior, estamos restringiendo el acceso al endpoint POST para /testentity a usuarios con el rol “ADMIN”, mientras que permitimos que cualquier usuario autenticado acceda a todos los demás endpoints. Si un usuario no autenticado intenta acceder a cualquier endpoint, se le pedirá que inicie sesión utilizando la autenticación básica de HTTP.

    Para obtener los roles del usuario de la sesión, puedes crear una implementación personalizada de UserDetailsService que recupere los roles de la sesión y cree un objeto UserDetails con las autoridades adecuadas.

    Por ejemplo:

    @Service
    public class CustomUserDetailsService implements UserDetailsService {
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                    .getRequest().getSession();
    
            User user = (User) session.getAttribute("user");
    
            if (user == null) {
                throw new UsernameNotFoundException("Usuario no encontrado");
            }
    
            List<grantedauthority> authorities = user.getRoles().stream()
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
    
            return new org.springframework.security.core.userdetails.User(
                    user.getUsername(),
                    user.getPassword(),
                    authorities);
        }
    }
    

    En la implementación anterior, estamos utilizando RequestContextHolder de Spring para obtener el HttpServletRequest actual y recuperar la sesión de él. Luego, recuperamos el objeto User de la sesión y creamos un objeto UserDetails con las autoridades basadas en los roles del usuario.

    Finalmente, debemos configurar Spring para que use nuestro UserDetailsService personalizado agregando lo siguiente a nuestra clase de configuración de seguridad:

    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    

    Con estas configuraciones en su lugar, Spring Security restringirá el acceso al endpoint POST para /testentity a usuarios con el rol “ADMIN”, mientras permite que cualquier usuario autenticado acceda a todos los demás endpoints. Los roles para cada usuario se recuperan de la sesión utilizando una implementación personalizada de UserDetailsService.

Comments are closed.