Al desarrollar aplicaciones, muchas veces una necesidad del negocio es securizar la aplicación y restringir las acciones que se pueden realizar en esta utilizando usuarios y roles.
Con SpringSecurity, rápidamente podemos securizar nuestra aplicación siguiendo una serie de pasos.
Antes de comenzar, vamos a tener que tener importado en nuestro proyecto el módulo de Spring Data JPA y debemos tener configurada una conexión a una base de datos desde nuestra aplicación.
Aclarado esto, comencemos.
Importar Spring Security
En primer lugar debemos agregar el módulo de Security a nuestro pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Con hacer esto automáticamente la aplicación estará configurada con una pantalla de LOGIN que nos provee el módulo. Para poder ingresar a la aplicación, esta misma genera una password que imprime en la consola y el usuario por default es user
Crear usuarios y Roles
Para comenzar, vamos a definir que roles vamos a manejar dentro de nuestra aplicación. Hay diversas maneras de crear los roles, pero en este caso como son roles muy puntuales los manejaremos desde la aplicación en un Enum.
public enum Rol {
ROLE_ADMIN,
ROLE_USER,
ROLE_GUESS;
}
Vamos a crear la clase usuario con los atributos username, password y rol. Esta clase debe implementar la interfaz UserDetails la cual se encarga de proveer los datos indispensables de un usuario.
@Entity
public class Usuario implements UserDetails{
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
private String username;
private String password;
@Enumerated(EnumType.STRING)
private Rol rol;
//Getters y Setters
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority(rol.toString()));
return roles;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Para obtener los usuarios, utilizaremos un Service que se conectará a un repositorio que implementa la interfaz JPA para acceder a nuestra base de datos.
Para que el Service funcione, tiene que estar anotado como @Service y debe implementar la interfaz UserDetailsService.
La interfaz UserDetailsService se utiliza para recuperar datos relacionados con el usuario. Tiene un método llamado loadUserByUsername () que se puede sobreescribir para personalizar el proceso de búsqueda del usuario.
@Service
public class UserDetailsServiceImpl implements UserDetailsService{
@Autowired
private UsuarioRepository usuarioRepository;
@Override
public UserDetails loadUserByUsername(String username){
return usuarioRepository.findByUsername(username);
}
}
El repositorio debe ser una interfaz y debe implementar la interfaz JpaRepository
public interface EmpresaRepository extends JpaRepository<Empresa,Long>{
public Empresa findByNombre(String nombre);
}
Configurar Spring Security
Para poder realizar la configuración, debemos crear una clase que extienda de WebSecurityConfigurerAdapter y tiene que estar anotada como @Configuration y @EnableWebSecurity. Lo que haremos será sobreescribir el método configure para poder configurar nosotros el AuthenticationManagerBuilder que es el objeto que se encargará de realizar el manejo de la autenticación de usuarios.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
Encriptar Password
Para securizar los datos de los usuarios, vamos a encriptar la password utilizando el objeto que nos provee Spring Security llamado BcryptPasswordEncoder en la clase SecurityConfig. Con @Bean vamos a crear el factory method para poder instanciar el encoder. Y para poder utilizarlo, se lo setearemos con el metodo passwordEncoder al userDetailService
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
Restringir accesos
Por último y para concluir con el post, la idea sería que apliques tu propia lógica a la hora de restringir el acceso por roles a los usuarios. Para esto, en la clase SecurityConfig,se sobreescribe el método configure para configurar la entidad http y con ello las url de tu página. En el siguiente ejemplo, sólo los usuarios con rol ADMIN podrán ingresar a la ruta ".../admin"
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole(ROLE_ADMIN)
.and()
.formLogin();
}
Y eso fue todo. Espero que les sirva este artículo tanto como me sirvió a mi escribirlo. Existen muchas maneras de realizar este proceso pero eso les queda a ustedes investigar y decidir que solución se adapta más a la problemática que deben resolver.¡Muchas gracias por leer!