Tips, Soluciones y Novedades en Tecnología

06/03/2020

Spring Security Rest





Spring Security es muy potente para gestionar la seguridad de nuestras aplicaciones, adopta muchas buenas prácticas tal como podemos ver en su documentación.



La configuración hasta la versión 3.x se realizaba por XML, posteriores versiones se realiza por anoraciones fascilitando su configuración.









Spring Security permite muchas formas de configurar la validación  de acceso, desde LDAP, hasta validación en memoria con fines de pruebas.





En este Post vamos a ver un escenario muy particular sobre como podemos lograr la validación de Spring Security desde un controllador Rest, para no tener que configurar filtros, datasources y otras configuraciones que hacen nuestro código tenga muchas configuraciones, ademas de simplificar la autenticación.





Lo primero que crearemos es la configuración de Spring Security, WebSecurityConfig.java








package com.dev.segurity.apisecurity.config;

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;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors().disable();
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/login/**").permitAll()
.antMatchers("/**").authenticated()
.and()
.logout()
.logoutSuccessUrl("/");
}
}




Como podemos ver nuestra configuración de Spring Security es bastante limpia.



Solo permitidos acceso a /api/login para poder hacer la autenticación por esa ruta, habilitamos tambien la ruta /logout para cerrar sesión automaticamente por parte de spring security.





Ahora tenemos que crear 2 DTOs (Data Transfer Object) para gestionar nuestras credenciales y nuestra sesión.



Lo crearemos en el paquete "dto", Credential.java y SessionInfo.java.



Credential.java es para recibir el usuario y clave desde el cliente y SessionInfo.java es para almacenar en la sessión de Spring Security, a esta clase le podemos agregar mas atributos en función a lo que queremos guardar.









Como paso siguiente, tenemos que agregar un nuevo paquete "session" y crear 3 clases Privilege.java, Role.java y SessionManager.java, las 2 primeras es para utilizar como auxiliar en la sesión, tal como podemos ver en la imagen siguiente.











La clase SessionManager.java es la encargada en injectar y obtener la sesión del contexto Spring Security.






package com.dev.segurity.apisecurity.session;

import com.dev.segurity.apisecurity.dto.SessionInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class SessionManager {

private static final SessionManager instance = new SessionManager();
public static SessionManager getInstance() {
return instance;
}
public void add(SessionInfo info) {
Role r = new Role();
r.setName("ROLE_ADMIN");
List<Role> roles = new ArrayList<>();
roles.add(r);
Collection<? extends GrantedAuthority> authorities = roles;
Authentication nwahtu = new UsernamePasswordAuthenticationToken(info, info.getUsername(), authorities);
SecurityContextHolder.getContextHolderStrategy().getContext().setAuthentication(nwahtu);
}

public SessionInfo get() {
SessionInfo info = null;
Authentication auth = SecurityContextHolder.getContextHolderStrategy().getContext().getAuthentication();
if (auth != null) {
info = (SessionInfo) auth.getPrincipal();
}
return info;
}
}




Esta clase lo es todo, es el punto de acceso a Spring Security, y podemos gestionar nuestra sesión desde cualquier componente, solo llamando esta clase singleton.





Ahora tenemos que crear dos controllers para probar, uno LoginController.java que nos permita recibir por Rest el usuario y clave, realizar la validación y llamar a SessionManager.java para inyectar la sessión, tal como vemos a continuación.






package com.dev.segurity.apisecurity.controller;

import com.dev.segurity.apisecurity.dto.Credential;
import com.dev.segurity.apisecurity.dto.SessionInfo;
import com.dev.segurity.apisecurity.session.SessionManager;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class LoginController {

@RequestMapping(method = RequestMethod.POST, path = "/login")
public ResponseEntity<Object> login(HttpServletRequest request, @RequestBody Credential dto) {
SessionInfo info = new SessionInfo();
if (dto.getUsuario().equals("admin") && dto.getClave().equals("admin")) {
info.setUsername(dto.getUsuario());
info.setIp(request.getRemoteAddr());
SessionManager.getInstance().add(info);
}
return new ResponseEntity<>(info, HttpStatus.UNAUTHORIZED);
}
}






Como podemos observar se recibió por POST el usuario y clave, se valida y se instancia la clase SessionInfo,java, ademas se agrega la IP y bien se podria en este punto ir a un repositorio y hacer la validación con la base de datos para hacer la validación.





Ahora vamos a ver el controller SessionController.java que solo se encarga de consultar la sesión.






package com.dev.segurity.apisecurity.controller;

import com.dev.segurity.apisecurity.session.SessionManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class SessionController {
@RequestMapping(path = "/info")
public ResponseEntity<Object> login() {
return new ResponseEntity<>(SessionManager.getInstance().get(), HttpStatus.OK);
}
}






Este servicio solo funcionará si existe la sesión, caso contrario mostrará error.





A continuación tenemos que probar nuestra autenticación por REST a Spring Security e injectarlo manualmente.





 Autenticando con REST















Obteniendo la sesión sin autenticar.









Obteniendo la sesión autenticado.













Como se pudo ver, Spring permite múltiples formas de autenticar, en este Post se ha realizado la autenticación por REST y se ha injectado y obtenido la sesión directamente del contexto de Spring secuurity.





El codigo lo pueden obtener desde GitHub.



Saludos.







0 comments:

Publicar un comentario