Los microservicios, como pequeños módulos autónomos, interactúan entre sí para construir sistemas complejos. La seguridad juega un papel clave aquí, porque los microservicios intercambian datos que pueden ser confidenciales. Sin una configuración de acceso correcta es fácil provocar fugas, errores de permisos y, como consecuencia, pérdida de la confianza de los usuarios.
Para trabajar con microservicios, Spring Security ofrece mecanismos flexibles de protección y gestión de accesos. Vamos a ver juntos cómo hacer que nuestros sistemas basados en microservicios no solo funcionen, sino que también sean seguros.
Pues nada, activamos Spring Security y discutimos los enfoques y las buenas prácticas.
Un poco de teoría
Para empezar, repasemos brevemente las necesidades de seguridad en microservicios:
- Autorización y autenticación. Necesitamos dejar claro quién hace la petición y si ese "quién" tiene permiso para acceder al recurso.
- Gestión de roles y accesos. Por ejemplo, en nuestra app los administradores ven más datos que los usuarios normales.
- Interacción entre servicios. Un microservicio debe estar seguro de que una petición de otro microservicio realmente viene de un servicio de confianza.
En arquitectura de microservicios a menudo dividimos la seguridad en dos niveles:
- Acceso externo — peticiones del cliente (usuario) a los microservicios.
- Comunicaciones internas — interacción entre microservicios.
Para resolver los retos de seguridad vamos a usar una combinación de Spring Security y JWT — la pareja ideal para una protección robusta y una configuración ágil.
Parte práctica: configuración de seguridad en Spring Security
Paso 1: Añadimos Spring Security Para empezar, agreguemos la dependencia de Spring Security en nuestro pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Esta dependencia mete en la aplicación todo lo necesario para la configuración básica de seguridad.
Paso 2: Creamos la configuración de seguridad
Para microservicios normalmente empezamos con una configuración basada en Java. Creamos una clase donde configuremos Spring Security.
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // Deshabilitamos CSRF para simplificar (¡solo para API!)
.authorizeRequests()
.antMatchers("/public/**").permitAll() // Acceso público
.antMatchers("/admin/**").hasRole("ADMIN") // Solo para roles ADMIN
.anyRequest().authenticated() // Todas las demás peticiones deben estar autenticadas
.and()
.httpBasic(); // Usamos autenticación básica (para empezar)
}
}
Qué hicimos aquí:
- Deshabilitamos CSRF (esto es importante porque trabajamos con REST API). De todos modos, ojo — en producción hay que tener en cuenta CSRF.
- Configuramos rutas públicas y protegidas. Ahora
/public/**está abierto para todos, y/admin/**solo para administradores.
Paso 3: Añadimos usuarios en memoria
Ahora añadiremos una configuración simple para guardar usuarios en memoria. Es útil en desarrollo para montar el acceso rápido.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER") // {noop} significa que la contraseña no está encriptada
.and()
.withUser("admin").password("{noop}admin").roles("ADMIN");
}
Ahora tenemos dos usuarios: "user" con contraseña "password", que pertenece al rol USER, y "admin" con contraseña "admin", que es ADMIN.
Paso 4: Añadimos JWT
Ahora vamos a añadir tokenización con JWT. JWT sustituirá la autenticación básica y nos permitirá gestionar accesos de forma más flexible.
Paso 4.1: Añadimos dependencias para JWT
Actualizamos el pom.xml para añadir la librería para trabajar con JWT:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Paso 4.2: Creamos una utilidad para trabajar con JWT
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "super_secret_key";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 horas
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String validateToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody().getSubject();
}
}
Paso 4.3: Configuramos un filtro para la autenticación JWT
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
public JwtFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String username = jwtUtil.validateToken(token);
if (username != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
Resumen
Ahora nuestra aplicación está lista para trabajar con JWT para la autenticación y la protección de microservicios. Puedes probar la API con herramientas como Postman o curl, enviando el JWT con las peticiones. ¡Adiós, inyecciones SQL en los encabezados de las peticiones! Spring Security está aquí para mantener todo seguro.
GO TO FULL VERSION