JWT es un mecanismo basado en JSON que nos permite crear tokens de acceso que nos permiten segurizar las comunicaciones entre cliente y servidor.
Veamos una breve explicación de lo que puede servirnos este mecanismo.
Imaginemos que tenemos un servicio muy simple que manda un mensaje de "Hola, mundo con Spring Boot". Ahora bien, queremos que solo un usuario autenticado y con permisos pueda acceder a el. Para eso esta JWT, para auxiliarnos en este caso.
Pasos:
1. El cliente tendrá que autenticarse con su id de usuario y contraseña.
2. Si el cliente se logró autenticar correctamente, se deberá generar un token.
3. Usaremos ese token para acceder al recurso (el servicio que manda el "Hola, mundo con Spring Boot"). El token que se ha generado se desencriptará y validará. Permitiendo al cliente acceder al recurso.
Requerimientos:
- Tener Maven instalado, de referencia última versión.
- Tener el JDK , de referencia última versión.
- Tener conocimientos de Java y Spring Boot.
1. Comencemos creando un proyecto Spring Boot con lo minímo integrado. Podemos entrar a este sitio para crearlo:
https://start.spring.io/Aún no colocaremos las dependendias de jwt y security. Con que tengamos lo básico es suficiente.
Nuestra clase principal lucirá así:
HolaMundoApplication.java
package com.inforhomex.holamundo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HolaMundoApplication { public static void main(String[] args) { SpringApplication.run(HolaMundoApplication.class, args); } }
Compilemos y echemosla a andar:
$ mvn clean install $ mvn spring-boot:run
Salida:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v4.0.1) 2026-01-11T17:55:53.773-06:00 INFO 16804 --- [HolaMundo] [ main] c.i.holamundo.HolaMundoApplication : Starting HolaMundoApplication using Java 21.0.1 w
Esto es solo para ver que funciona. Aún no tiene una clase Controller que haga realmente algo.
2. Crearemos una clase Controller que tenga un mensaje sencillo de "Hola, mundo con Spring Boot".
HolaMundoController.java
package com.inforhomex.holamundo.controller; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @RestController @RequestMapping("/api") public class HolaMundoController { @GetMapping("/saludo") public String saludo() { return "Hola, mundo con Spring Boot."; } }
Es importante notar que nos basamos en una arquitectura Java modular. Es decir, usamos paquetes. En este caso la clase tipo Controller ``HolamundoController.java`` estará contenida en el paquete controller. Quien ya haya trabajado con Spring Framework (Spring Boot) y Java sabrá a que me refiero.
3. Editemos el archivo ``src\main\resources\application.properties`` de tal manera que quede así:
spring.application.name=HolaMundo server.port=8083
Con esto indicamos el puerto que queremos usar. Echemos a andar la aplicación:
$ mvn spring-boot:run
Abrimos el navegador en la ruta: http://localhost:8083/api/saludo
Salida:
Hola, mundo con Spring Boot.
Hemos creado un sencillo servicio. Pero, ¿Qué es lo que sigue? Lo que sigue es:
- Proteger ese servicio.
- Obligar al cliente a autenticarse (con su usuario y contraseña).
- Si es un cliente válido, se generará un token de acceso.
- El cliente usará ese token para acceder al servicio. Si esta autorizado, podrá acceder.
Es aquí donde empieza lo interesante o como lo dice el meme:
Aquí entra el mecanismo de JWT. En una aplicación de este tipo se debe contar con:
- Un servidor de autenticación.
- Un servidor de autorización.
- Un servidor para nuestro(s) servicio(s).
Aquí solo usaremos uno para las tres funcionalidades.
Recordemos la composición de un token JWT:
Header.Payload.Signature
Donde:
Header:{ "alg": "HS256", "typ": "JWT" }
{ "username": "admin", "password": "s3cr3t4" }
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
El token JWT se puede ver así:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 .eyJ1c2VySWQiOjEsInJvbGUiOiJhZG1pbiJ9 .sflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
4. Agregamos las dependencias necesarias en el archivo ``pom.xml``:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.12.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.12.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.12.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
5. Creamos una clase Controller para la autenticación, que valida y genera el token:
AuthController.java
package com.inforhomex.holamundo.controller; import com.inforhomex.holamundo.security.JwtUtil; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController @RequestMapping("/api/auth") public class AuthController { private final AuthenticationManager authenticationManager; public AuthController(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @PostMapping("/login") public Map<String, String> login(@RequestBody Map<String, String> request) { Authentication auth = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( request.get("username"), request.get("password") ) ); String token = JwtUtil.generarToken(auth.getName()); return Map.of("token", token); } }
6. Crearemos una clase para la configuración del usuario:
UserConfig.java
package com.inforhomex.holamundo.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class UserConfig { @Bean public UserDetailsService userDetailsService() { return new InMemoryUserDetailsManager( User.withUsername("admin") .password(passwordEncoder().encode("1234")) .roles("USER") .build() ); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Esta clase solo nos sirve para un usuario "admin" y contraseña fija.
7. Crearemos una clase para la seguridad de la aplicación:
SecurityConfig.java
package com.inforhomex.holamundo.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration public class SecurityConfig { @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() ) .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
Esta clase
8. Ahora con los filtros y utilidades Jwt.
JwtUtil.java
package com.inforhomex.holamundo.security; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; public class JwtUtil { private static final SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); public static String generarToken(String username) { return Jwts.builder() .subject(username) .issuedAt(new Date()) .expiration(new Date(System.currentTimeMillis() + 3600000)) .signWith(key) .compact(); } public static String obtenerUsuario(String token) { return Jwts.parser() .verifyWith(key) .build() .parseSignedClaims(token) .getPayload() .getSubject(); } }
JwtFilter.java
package com.inforhomex.holamundo.security; import jakarta.servlet.FilterChain; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; import java.util.Collections; public class JwtFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain ) throws IOException, jakarta.servlet.ServletException { String header = request.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { String token = header.substring(7); String usuario = JwtUtil.obtenerUsuario(token); var auth = new UsernamePasswordAuthenticationToken( usuario, null, Collections.emptyList()); SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } }
9. Compilamos la aplicación:
$ mvn clean install
5. Ejecutamos la aplicación:
$ mvn spring-boot:run
Si todo va bien, se ejecutará la aplicación.
Consumiendo el servicio con Powershell
Podemos consumir el servicio con la ayuda de Powershell.
1. Programa que obtenga el token:
get_toke.ps1
$body = @{ username = "admin" password = "1234" } | ConvertTo-Json $response = Invoke-RestMethod ` -Uri "http://localhost:8083/api/auth/login" ` -Method Post ` -ContentType "application/json" ` -Body $body $response.token
Ejecutamos el script:
$ .\get_token.ps1
2. Programa use el token para acceder al servicio:
use_token.ps1
$token = "AQUI_VA_EL_TOKEN_OBTENIDO" write-host "Token: $token" $headers = @{ Authorization = "Bearer $token" } $response = Invoke-RestMethod -Uri "http://localhost:8083/api/saludo" -Headers $headers -Method Get Write-Host "Respuesta del servicio protegido:" $response
Ejecutamos el script:
$ .\use_token.ps1
Si todo va bien, veremos el mensaje:
Hola, mundo con Spring Boot.
Este es un ejemplo "sencillo" para entender la lógica de JWT.
Continuaremos con esta serie de entregas sobre Spring Boot y JWT.
Enlaces:
https://blog.softtek.com/es/autenticando-apis-con-spring-y-jwthttps://www.javainuse.com/spring/boot-jwt
https://www.jwt.io/


Comentarios
Publicar un comentario