En este artículo, vamos a desarrollar un sistema cliente-servidor avanzado en Java que incluye funcionalidades de autenticación de usuarios y gestión de sesiones. Este proyecto te permitirá aplicar conceptos avanzados de programación en red, como la autenticación de usuarios, la gestión de sesiones mediante tokens, y la implementación de seguridad básica en un entorno cliente-servidor.
Parte 1: Implementación del Servidor HTTP en Java con Autenticación y Sesiones
El servidor que implementaremos se encargará de autenticar a los usuarios y manejar sesiones utilizando tokens únicos. Los usuarios podrán iniciar sesión proporcionando un nombre de usuario y una contraseña. Si la autenticación es exitosa, el servidor devolverá un token de sesión que el cliente utilizará para acceder a recursos protegidos.
Código del Servidor:
package cristinaleonacademiaexamen;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
public class ServidorHTTP {
private static Map<String, String> usuarios = new HashMap<>();
private static Map<String, String> sesiones = new HashMap<>();
public static void main(String[] args) throws IOException {
usuarios.put("usuario1", "password1");
usuarios.put("usuario2", "password2");
HttpServer server = HttpServer.create(new InetSocketAddress(8083), 0);
server.createContext("/login", new LoginHandler());
server.createContext("/recurso", new RecursoProtegidoHandler());
server.setExecutor(Executors.newCachedThreadPool()); // Manejo concurrente de solicitudes
server.start();
System.out.println("Servidor HTTP iniciado en el puerto 8083");
}
static class LoginHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
if ("POST".equals(exchange.getRequestMethod())) {
// Obtener las credenciales de la solicitud
String[] credenciales = new String(exchange.getRequestBody().readAllBytes()).split("&");
String nombreUsuario = credenciales[0].split("=")[1];
String password = credenciales[1].split("=")[1];
// Verificar las credenciales
if (usuarios.containsKey(nombreUsuario) && usuarios.get(nombreUsuario).equals(password)) {
// Generar un token de sesión
String token = UUID.randomUUID().toString();
sesiones.put(token, nombreUsuario);
// Responder con el token de sesión
String respuesta = "Autenticación exitosa. Token de sesión: " + token;
exchange.sendResponseHeaders(200, respuesta.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(respuesta.getBytes());
os.close();
} else {
String respuesta = "Credenciales incorrectas.";
exchange.sendResponseHeaders(401, respuesta.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(respuesta.getBytes());
os.close();
}
} else {
exchange.sendResponseHeaders(405, -1); // Método no permitido
}
}
}
static class RecursoProtegidoHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String token = exchange.getRequestHeaders().getFirst("Authorization");
if (sesiones.containsKey(token)) {
String nombreUsuario = sesiones.get(token);
String respuesta = "Acceso concedido al recurso protegido para el usuario: " + nombreUsuario;
exchange.sendResponseHeaders(200, respuesta.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(respuesta.getBytes());
os.close();
} else {
String respuesta = "No autorizado. Token de sesión inválido.";
exchange.sendResponseHeaders(403, respuesta.getBytes().length);
OutputStream os = exchange.getResponseBody();
os.write(respuesta.getBytes());
os.close();
}
}
}
}
Explicación del Código:
- Gestión de Usuarios y Sesiones:
usuarios
: UnHashMap
que almacena los nombres de usuario y las contraseñas.sesiones
: UnHashMap
que almacena los tokens de sesión asociados a los nombres de usuario.
- Autenticación de Usuarios:
- La ruta
/login
maneja solicitudes POST que contienen las credenciales del usuario. Si las credenciales son correctas, se genera un token de sesión único que se devuelve al cliente.
- Acceso a Recursos Protegidos:
- La ruta
/recurso
maneja solicitudes GET que requieren un token de sesión válido en la cabeceraAuthorization
. Si el token es válido, se concede acceso al recurso protegido.
- Concurrencia:
- Se utiliza un
Executor
para manejar múltiples solicitudes de manera concurrente.
Parte 2: Implementación del Cliente Java con Autenticación y Gestión de Sesiones
El cliente Java se encargará de autenticar a los usuarios y acceder a recursos protegidos utilizando el token de sesión proporcionado por el servidor.
Código del Cliente:
package cristinaleonacademiaexamen;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class ClienteHTTP {
public static void main(String[] args) {
try {
// Autenticación del usuario
String urlLogin = "http://localhost:8083/login";
String credenciales = "usuario1=password1";
String token = autenticarUsuario(urlLogin, credenciales);
System.out.println("Token de sesión: " + token);
// Acceso a recurso protegido usando el token
String urlRecurso = "http://localhost:8083/recurso";
String respuestaRecurso = accederRecursoProtegido(urlRecurso, token);
System.out.println("Respuesta del recurso protegido: " + respuestaRecurso);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String autenticarUsuario(String urlStr, String credenciales) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conexion = (HttpURLConnection) url.openConnection();
conexion.setRequestMethod("POST");
conexion.setDoOutput(true);
try (OutputStream os = conexion.getOutputStream()) {
os.write(credenciales.getBytes());
os.flush();
}
BufferedReader in = new BufferedReader(new InputStreamReader(conexion.getInputStream()));
String inputLine;
StringBuilder contenido = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
contenido.append(inputLine);
}
in.close();
return extraerTokenDeRespuesta(contenido.toString());
}
private static String extraerTokenDeRespuesta(String respuesta) {
int inicio = respuesta.indexOf("Token de sesión: ") + 17;
return respuesta.substring(inicio);
}
private static String accederRecursoProtegido(String urlStr, String token) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conexion = (HttpURLConnection) url.openConnection();
conexion.setRequestMethod("GET");
conexion.setRequestProperty("Authorization", token);
BufferedReader in = new BufferedReader(new InputStreamReader(conexion.getInputStream()));
String inputLine;
StringBuilder contenido = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
contenido.append(inputLine);
}
in.close();
return contenido.toString();
}
}
Explicación del Código:
- Autenticación del Usuario:
- El cliente envía una solicitud POST al servidor con las credenciales del usuario. Si la autenticación es exitosa, recibe un token de sesión que se utilizará para acceder a recursos protegidos.
- Acceso a Recursos Protegidos:
- El cliente envía una solicitud GET a un recurso protegido, incluyendo el token de sesión en la cabecera
Authorization
. Si el token es válido, el servidor responde con el contenido del recurso.
- Métodos Auxiliares:
autenticarUsuario
: Envía las credenciales del usuario al servidor y obtiene el token de sesión.extraerTokenDeRespuesta
: Extrae el token de sesión de la respuesta del servidor.accederRecursoProtegido
: Envía una solicitud GET al recurso protegido utilizando el token de sesión.
Conclusión
Este ejercicio avanzado implementa un sistema cliente-servidor en Java con funcionalidades de autenticación y gestión de sesiones. El servidor maneja la autenticación de usuarios y la emisión de tokens de sesión, que se utilizan para acceder a recursos protegidos. El cliente, por su parte, interactúa con el servidor para autenticar usuarios y acceder a recursos utilizando el token.
Este sistema proporciona una base sólida para el desarrollo de aplicaciones seguras en Java que requieren autenticación y manejo de sesiones, y puede ser extendido para incluir características adicionales como encriptación, caducidad de tokens, y autenticación de múltiples factores. ¡Espero que este ejercicio te haya sido útil y que te ayude a mejorar tus habilidades en programación de sistemas distribuidos en Java!
Contenido restringido
Comments are closed