Introducción: por qué tu contraseña no es solo una cadena
\n\nImagina: escribiste una aplicación Java genial para una biblioteca en línea. Los usuarios se registran, guardan marcadores, escriben reseñas. Y de repente — una fuga de la base de datos. Todas las contraseñas en texto plano. ¿Catástrofe? ¡Y de qué manera! En 2024, las filtraciones de datos ocurren cada 39 segundos, y el 81% de ellas están relacionadas con una autenticación débil. Si programas en Java y quieres que tu proyecto no termine en los titulares de cibernoticias — este artículo es para ti.
\n\nAnalizaremos la autenticación segura en aplicaciones Java de principio a fin: desde el hash de contraseñas hasta JWT y la protección contra CSRF. Sin teoría aburrida, solo código funcional y prácticas reales. ¡Vamos allá!
\n\n1. Hash de contraseñas: la primera línea de defensa
\n\nLa primera regla de seguridad: nunca almacenes contraseñas en texto plano. Incluso si tu base de datos es el santo grial, el cifrado de contraseñas es el mínimo obligatorio. En Java se utilizan algoritmos como bcrypt, scrypt o Argon2. ¿Por qué no MD5 o SHA? Porque son demasiado rápidos — un atacante podría descifrar la contraseña en segundos.
\n\nPráctica: bcrypt en acción
\nAñade la dependencia en pom.xml (Maven):
<dependency>\n <groupId>org.springframework.security</groupId>\n <artifactId>spring-security-crypto</artifactId>\n <version>6.2.0</version>\n</dependency>\n\n\nAhora hasheamos la contraseña al registrarse:
\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\n\npublic class PasswordService {\n private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\n public String hashPassword(String rawPassword) {\n return encoder.encode(rawPassword); // genera sal automáticamente\n }\n\n public boolean checkPassword(String rawPassword, String storedHash) {\n return encoder.matches(rawPassword, storedHash);\n }\n}\n\n\nImportante: bcrypt añade una sal aleatoria por sí mismo, por lo que incluso contraseñas idénticas se verán diferentes. El factor de costo (cost factor) por defecto es 10, pero para sistemas críticos se puede subir a 12-14.
\n\n2. JWT (JSON Web Tokens): sesión ligera y segura
\n\nDespués del registro, el usuario debe confirmar su identidad de alguna manera. ¿Cookies? Se puede, pero JWT es el estándar moderno para APIs REST. El token contiene información sobre el usuario y está firmado con una clave secreta. Sin sesiones en el servidor — todo está en el token.
\n\nCreación de JWT en Java
\nConecta la librería io.jsonwebtoken:jjwt:
<dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-api</artifactId>\n <version>0.12.3</version>\n</dependency>\n\n\nGeneración del token:
\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.security.Keys;\nimport javax.crypto.SecretKey;\nimport java.util.Date;\n\npublic class JwtUtil {\n private final SecretKey key = Keys.hmacShaKeyFor("clave_secreta_de_al_menos_256_bits".getBytes());\n\n public String generateToken(String username) {\n return Jwts.builder()\n .subject(username)\n .issuedAt(new Date())\n .expiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hora\n .signWith(key)\n .compact();\n }\n\n public String extractUsername(String token) {\n return Jwts.parser()\n .verifyWith(key)\n .build()\n .parseSignedClaims(token)\n .getPayload()\n .getSubject();\n }\n}\n\n\nConsejo: ¡no almacenes la clave secreta en el código! Usa variables de entorno o Vault. Y nunca pongas datos confidenciales en JWT (contraseñas, números de tarjeta) — el token se puede decodificar sin la firma.
\n\n