Tecnologías Utilizadas
Framework Backend: SpringBoot
SpringBoot integra servidores como Tomcat, Jetty y Undertow, permitiendo su uso sin configuraciones adicionales. Una de sus características principales es la configuración automática, que adapta la aplicación según las dependencias del proyecto. Esto simplifica significativamente el proceso de configuración, eliminando la necesidad de ajustar manualmente cada dependencia.
El framework ofrece numerosas funcionalidades listas para usar, como Spring Data para el acceso a datos, Spring Security para la autenticación y Spring Cloud para arquitecturas distribuidas. Estas herramientas aceleran el desarrollo y facilitan la integración con otras tecnologías. Su popularidad se debe a estas características, que permiten construir aplicaciones de alta calidad de manera eficiente y con menos esfuerzo.
Framework Front end: Vue
Vue.js utiliza tecnología de DOM Virtual, una estructura de datos en memoria que optimiza las operaciones con el DOM. Implementa enlace de datos reactivo, DOM Virtual y arquitectura de componentes, proporcionando un modelo de desarrolllo flexible, eficiente y fácil de mantener.
Cuando los datos cambian, la interfaz de usuario se actualiza automáticamente, permitiendo a los desarrolladores concentrarse en la lógica de datos en lugar de en las actualizaciones manuales de la interfaz. Esta es la esencia de la simplicidad, flexibilidad y eficiencia que Vue.js ofrece.
Capa de Persistencia: MyBatisPlus
MyBatis-Plus es una herramienta de extensión basada en MyBatis diseñada para simplificar el desarrollo. Es un framework Java de código abierto compatible con múltiples bases de datos como MySQL, Oracle, SQL Server y PostgreSQL.
Proporciona una amplia API y anotaciones que facilitan las operaciones ORM, reduciendo considerablemente la necesidad de escribir SQL manualmente. Además, incluye un generador de código que puede crear automáticamente entidades, interfaces Mapper y archivos de mapeo XML, optimizando el flujo de desarrollo.
MyBatis-Plus también soporta funcionalidades prácticas como paginación, consultas dinámicas, bloqueo optimista y análisis de rendimiento, permitiendo operaciones de datos eficientes. Con esta herramienta, los desarrolladores pueden crear capas de acceso a datos de alta calidad rápidamente.
Pruebas del Sistema
El objetivo principal de las pruebas es identificar problemas desde múltiples perspectivas. A través de las pruebas funcionales, se detectan y corrigen defectos, asegurando que el sistema cumpla con los requisitos del cliente. Durante el proceso de prueba, se valida que el sistema satisface las necesidades del usuario y se corrigen oportunamente los problemas encontrados.
Objetivo de las Pruebas del Sistema
En el ciclo de desarrollo de aplicaciones web, las pruebas del sistema son un proceso esencial que requiere paciencia. Su importancia radica en ser el último filtro de calidad y fiabilidad, así como la última inspección de todo el proceso de desarrollo.
El objetivo es evitar problemas durante el uso del sistema y mejorar la experiencia del usuario. Es necesario cnosiderar múltiples ángulos y enfoques para anticipar posibles problemas, simulando diferentes escenarios para identificar y resolver deficiencias. Durante las pruebas, se evalúa la calidad del sistema, la integridad de sus funciones y la fluidez de su lógica. Un proceso de pruebas adecuado mejora significativamente la calidad y la usabilidad del sistema. Las pruebas buscan verificar si el sistema cumple con las especificaciones del documento de requisitos e identificar cualquier inconsistencia o conflicto. Durante el proceso, es crucial adoptar la perspectiva del usuario para evitar escenarios poco realistas que consuman tiempo y puedan generar resultados inesperados.
Pruebas Funcionales del Sistema
Se realizan pruebas en los módulos funcionales del sistema mediante técnicas de caja negra, incluyendo clics, entrada de valores límite y validación de campos obligatorios y opcionales. Se elaboran casos de prueba basados en escenarios específicos y se documentan los resultados.
Casos de Prueba de Inicio de Sesión
| Datos de Entrada | Resultado Esperado | Resultado Real | Análisis |
|---|---|---|---|
| Usuario: admin Contraseña: 123456 Código: correcto | Iniciar sesión | Inicio de sesión exitoso | Coincide con lo esperado |
| Usuario: admin Contraseña: 111111 Código: correcto | Contraseña incorrecta | Mensaje de error de contraseña | Coincide con lo esperado |
| Usuario: admin Contraseña: 123456 Código: incorrecto | Código incorrecto | Mensaje de error de código | Coincide con lo esperado |
| Usuario: (vacío) Contraseña: 123456 Código: correcto | Usuario obligatorio | Mensaje de requerir usuario | Coincide con lo esperado |
| Usuario: admin Contraseña: (vacío) Código: correcto | Contraseña incorrecta | Mensaje de error de contraseña | Coincide con lo esperado |
Casos de Prueba de Gestión de Usuarios
La gestión de usuarios incluye funciones de adición, edición, eliminación y búsqueda. Al agregar un usuario, se valida que los campos obligatorios no estén vacíos; al agregar información de un usuario existente, se verifica si el sistema muestra una advertencia de duplicado; al eliminar un usuario, el sistema confirma la acción; al modificar información, los cambios se reflejan correctamente en la interfaz.
| Datos de Entrada | Resultado Esperado | Resultado Real | Análisis |
|---|---|---|---|
| Ingresar información básica del usuario | Agregar exitosamente, mostrar en lista | El usuario aparece en la lista | Coincide con lo esperado |
| Modificar información del usuario | Editar exitosamente, cambios aplicados | La información del usuario se actualiza | Coincide con lo esperado |
| Seleccionar y eliminar usuario | Sistema pregunta confirmación, usuario eliminado | Sistema pregunta confirmación, usuario no encontrado después | Coincide con lo esperado |
| No ingresar usuario al agregar | Mensaje de usuario obligatorio | Mensaje de usuario obligatorio | Coincide con lo esperado |
| Ingresar usuario existente | Fallo al agregar, mensaje de duplicado | Fallo al agregar, mensaje de duplicado | Coincide con lo esperado |
Conclusión de las Pruebas del Sistema
El sistema principalmente utiliza pruebas de caja negra, simulando el uso del sistema para verificar cada función y asegurar la corrección de los flujos. Las pruebas son indispensables para perfeccionar el sistema y mejorar su usabilidad.
Las pruebas buscan validar si los módulos funcionales cumplen con el diseño original y si su lógica es correcta. Este sistema no requiere procesamiento lógico complejo para facilitar su uso. El objetivo final de las pruebas se centra en la experiencia del usuario. Todos los escenarios de prueba deben satisfacer las necesidades del usuario sin desviarse de los objetivos iniciales. Al resolver problemas, es fundamental adoptar la perspectiva del usuario. Tras completar el proceso de prueba, los resultados indican que el sistema cumple con los requisitos funcionales y de diseño.
Código de Referencia
@IgnoreAuth
@PostMapping(value = "/iniciarSesion")
public R iniciarSesion(String nombreUsuario, String contrasena, String captcha, HttpServletRequest peticion) {
UsuarioEntity usuario = servicioUsuario.seleccionarUno(new EntityWrapper<UsuarioEntity>().eq("nombreUsuario", nombreUsuario));
if(usuario==null || !usuario.getContrasena().equals(contrasena)) {
return R.error("Cuenta o contraseña incorrectos");
}
String token = servicioToken.generarToken(usuario.getId(), nombreUsuario, "usuarios", usuario.getRol());
return R.ok().put("token", token);
}
@Override
public String generarToken(Long idUsuario, String nombreUsuario, String nombreTabla, String rol) {
TokenEntity entidadToken = this.seleccionarUno(new EntityWrapper<TokenEntity>().eq("idUsuario", idUsuario).eq("rol", rol));
String token = UtilidadesComunes.getCadenaAleatoria(32);
Calendar calendario = Calendar.getInstance();
calendario.setTime(new Date());
calendario.add(Calendar.HORA_DEL_DIA, 1);
if(entidadToken!=null) {
entidadToken.setToken(token);
entidadToken.setTiempoExpiracion(calendario.getTime());
this.actualizarPorId(entidadToken);
} else {
this.insertar(new TokenEntity(idUsuario, nombreUsuario, nombreTabla, rol, token, calendario.getTime()));
}
return token;
}
/**
* Verificación de Permisos (Token)
*/
@Component
public class InterceptorAutorizacion implements HandlerInterceptor {
public static final String CLAVE_TOKEN_LOGIN = "Token";
@Autowired
private ServicioToken servicioToken;
@Override
public boolean preHandle(HttpServletRequest peticion, HttpServletResponse respuesta, Object manejador) throws Exception {
// Soporte para solicitudes跨域
respuesta.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
respuesta.setHeader("Access-Control-Max-Age", "3600");
respuesta.setHeader("Access-Control-Allow-Credentials", "true");
respuesta.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
respuesta.setHeader("Access-Control-Allow-Origin", peticion.getHeader("Origin"));
// Las solicitudes跨域 envían primero una solicitud OPTIONS, respondemos con estado normal
if (peticion.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;
}
// Obtener token del encabezado
String token = peticion.getHeader(CLAVE_TOKEN_LOGIN);
/**
* Métodos que no requieren verificación de permisos
*/
if(anotacion!=null) {
return true;
}
TokenEntity entidadToken = null;
if(StringUtils.isNotBlank(token)) {
entidadToken = servicioToken.obtenerEntidadToken(token);
}
if(entidadToken != null) {
peticion.getSession().setAttribute("idUsuario", entidadToken.getIdUsuario());
peticion.getSession().setAttribute("rol", entidadToken.getRol());
peticion.getSession().setAttribute("nombreTabla", entidadToken.getNombreTabla());
peticion.getSession().setAttribute("nombreUsuario", entidadToken.getNombreUsuario());
return true;
}
PrintWriter escritor = null;
respuesta.setCharacterEncoding("UTF-8");
respuesta.setContentType("application/json; charset=utf-8");
try {
escritor = respuesta.getWriter();
escritor.print(JSONObject.toJSONString(R.error(401, "Por favor inicie sesión primero")));
} finally {
if(escritor != null){
escritor.close();
}
}
return false;
}
}
Estructura de Base de Datos de Referencia
-- ----------------------------
-- Estructura de la tabla token
-- ----------------------------
DROP TABLE IF EXISTS `token`;
CREATE TABLE `token` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Clave primaria',
`idUsuario` bigint(20) NOT NULL COMMENT 'ID de usuario',
`nombreUsuario` varchar(100) NOT NULL COMMENT 'Nombre de usuario',
`nombreTabla` varchar(100) DEFAULT NULL COMMENT 'Nombre de tabla',
`rol` varchar(100) DEFAULT NULL COMMENT 'Rol',
`token` varchar(200) NOT NULL COMMENT 'Contraseña',
`tiempoAdicion` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Tiempo de adición',
`tiempoExpiracion` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Tiempo de expiración',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Tabla de tokens';
-- ----------------------------
-- Registros de la tabla token
-- ----------------------------
INSERT INTO `token` VALUES ('9', '23', 'cd01', 'estudiante', 'Estudiante', 'al6svx5qkei1wljry5o1npswhdpqcpcg', '2023-02-23 21:46:45', '2023-03-15 14:01:36');
INSERT INTO `token` VALUES ('10', '11', 'xh01', 'estudiante', 'Estudiante', 'fahmrd9bkhqy04sq0fzrl4h9m86cu6kx', '2023-02-27 18:33:52', '2023-03-17 18:27:42');
INSERT INTO `token` VALUES ('11', '17', 'ch01', 'estudiante', 'Estudiante', 'u5km44scxvzuv5yumdah2lhva0gp4393', '2023-02-27 18:46:19', '2023-02-27 19:48:58');
INSERT INTO `token` VALUES ('12', '1', 'admin', 'usuarios', 'Administrador', 'h1pqzsb9bldh93m92j9m2sljy9bt1wdh', '2023-02-27 19:37:01', '2023-03-17 18:23:02');
INSERT INTO `token` VALUES ('13', '21', 'xiaohao', 'director', 'Director', 'zdm7j8h1wnfe27pkxyiuzvxxy27ykl2a', '2023-02-27 19:38:07', '2023-03-17 18:25:20');
INSERT INTO `token` VALUES ('14', '27', 'djy01', 'estudiante', 'Estudiante', 'g3teq4335pe21nwuwj2sqkrpqoabqomm', '2023-03-15 12:56:17', '2023-03-15 14:00:16');
INSERT INTO `token` VALUES ('15', '29', 'dajiyue', 'director', 'Director', '0vb1x9xn7riewlp5ddma5ro7lp4u8m9j', '2023-03-15 12:58:08', '2023-03-15 14:03:48');