Introducción
Este sistema de gestión de calificaciones está desarrollado para instituciones educativas, utilizando el marco de trabajo Java Spring Boot en el back end y Vue.js en el frontend. Permite la administración de notas estudiantiles con roles diferenciados: estudiantes, docentes y administradores. Los estudiantes pueden consultar sus calificaciones, los docentes tienen acceso para ingresar y modificar notas, y los administradores gestionan estadísticas y análisis generales.
Stack Tecnológico
Backend con Spring Boot
Spring Boot simplifica el desarrollo de aplicaciones Java al proporcionar configuración automática, servidores integrados como Tomcat y una amplia variedad de plugins. Esto reduce la necesidad de configuración manual y acelera el desarrollo de sistemas robustos y escalables.
Frontend con Vue.js
Vue.js utiliza una arquitectura basada en componentes y enlace de datos reactivo, lo que facilita la creación de interfaces de usuario dinámicas y mantenibles. Su enfoque en la virtual DOM permite actualizaciones eficientes del UI sin recargas innecesarias.
Persistencia de Datos con MyBatis Plus
MyBatis Plus extiende las capacidades de MyBatis, ofreciendo herramientas para operaciones ORM simplificadas, como generación de código automático y soporte para consultas dinámicas. Esto minimiza la escritura manual de SQL y mejora la productividad en el acceso a bases de datos MySQL.
Pruebas del Sistema
Las pruebas se enfocan en verificar la funcionalidad y la lógica del sistema mediante pruebas de caja negra. Se diseñaron casos de prueba para módulos clave como el inicio de sesión y la gestión de usuarios, validando respuestas ante entradas válidas e inválidas. Por ejemplo, en el módulo de autenticación, se prueba con credenciales correctas e incorrectas para asegurar la seguridad y el manejo adecuado de errores.
Ejemplo de Código: Autenticación y Autorización
@IgnoreAuth
@PostMapping("/autenticar")
public Respuesta autenticarUsuario(@RequestParam String usuario, @RequestParam String clave, @RequestParam String captcha, HttpServletRequest solicitud) {
EntidadUsuario entidadUsuario = servicioUsuario.buscarUno(new EnvolturaEntidad<EntidadUsuario>().eq("nombre_usuario", usuario));
if (entidadUsuario == null || !entidadUsuario.getClave().equals(clave)) {
return Respuesta.error("Credenciales inválidas");
}
String tokenGenerado = servicioToken.crearToken(entidadUsuario.getId(), usuario, "usuarios", entidadUsuario.getRol());
return Respuesta.ok().put("token", tokenGenerado);
}
@Override
public String crearToken(Long idUsuario, String nombreUsuario, String nombreTabla, String rol) {
EntidadToken tokenExistente = buscarUno(new EnvolturaEntidad<EntidadToken>().eq("id_usuario", idUsuario).eq("rol", rol));
String nuevoToken = UtilidadComun.cadenaAleatoria(32);
Calendar calendario = Calendar.getInstance();
calendario.setTime(new Date());
calendario.add(Calendar.HOUR_OF_DAY, 2);
if (tokenExistente != null) {
tokenExistente.setToken(nuevoToken);
tokenExistente.setFechaExpiracion(calendario.getTime());
actualizarPorId(tokenExistente);
} else {
insertar(new EntidadToken(idUsuario, nombreUsuario, nombreTabla, rol, nuevoToken, calendario.getTime()));
}
return nuevoToken;
}
@Component
public class InterceptorAutorizacion implements HandlerInterceptor {
public static final String CABECERA_TOKEN = "Authorization";
@Autowired
private ServicioToken servicioToken;
@Override
public boolean preHandle(HttpServletRequest solicitud, HttpServletResponse respuesta, Object manejador) throws Exception {
respuesta.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
respuesta.setHeader("Access-Control-Allow-Origin", solicitud.getHeader("Origin"));
if (solicitud.getMethod().equals(RequestMethod.OPTIONS.name())) {
respuesta.setStatus(HttpStatus.OK.value());
return false;
}
AnotacionIgnorarAuth anotacion;
if (manejador instanceof HandlerMethod) {
anotacion = ((HandlerMethod) manejador).getMethodAnnotation(AnotacionIgnorarAuth.class);
} else {
return true;
}
String tokenSolicitado = solicitud.getHeader(CABECERA_TOKEN);
if (anotacion != null) {
return true;
}
EntidadToken entidadToken = null;
if (StringUtils.isNotBlank(tokenSolicitado)) {
entidadToken = servicioToken.obtenerPorToken(tokenSolicitado);
}
if (entidadToken != null) {
solicitud.getSession().setAttribute("idUsuario", entidadToken.getIdUsuario());
solicitud.getSession().setAttribute("rol", entidadToken.getRol());
return true;
}
respuesta.setContentType("application/json; charset=UTF-8");
PrintWriter escritor = respuesta.getWriter();
escritor.print(JSONObject.toJSONString(Respuesta.error(401, "Autenticación requerida")));
escritor.flush();
return false;
}
}
Estructura de Base de Datos: Tabla de Sesiones
CREATE TABLE sesiones_token (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
id_usuario BIGINT NOT NULL,
nombre_usuario VARCHAR(100) NOT NULL,
nombre_tabla VARCHAR(100),
rol VARCHAR(100),
token VARCHAR(200) NOT NULL,
fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
fecha_expiracion TIMESTAMP NOT NULL
);
-- Ejemplo de inserción
INSERT INTO sesiones_token (id_usuario, nombre_usuario, nombre_tabla, rol, token, fecha_expiracion) VALUES
(23, 'cd01', 'estudiantes', 'estudiante', 'token_ejemplo_1', '2023-03-15 14:01:36'),
(1, 'admin', 'usuarios', 'administrador', 'token_ejemplo_2', '2023-03-17 18:23:02');