Como funcionan los interceptors en spring?

Que tal... he estado trabajando en una aplicacion cliente servidor sencillo y hasta el momento no habia tenido muchos problemas.

Las tecnologias que utilize fue spring para el lado server, utilizando un jdbc transaction y rmi exporter para los servicios, y para el cliente swing con su respectivo spring para la obtención de los servicios remotos vía rmi proxy.

Ahora viene mi problema. El tipo de autentificación es demasiado sencilla, solo se requiere que el password sea válido (ni si quiera un usuario se captura) y siempre se tienen todos los permisos. Ya eh utilizado acegi antes para la seguridad y pues esta bien, pero para mi caso es demasiado su funcionalidad así que busco algo mas sencillo.

El otro problema con la autentificación es que en el cuadro de login se captura una fecha, la cual será la fecha de trabajo durante toda la sessión, inclusive los servicios remotos deben trabajar con esta fecha, por lo que pense en un modelo donde con cada session se cree un row en una tabla de sesiones y que retorne un id al usuario, y que sea éste id el que ande pa alla y pa aca con cada llamada al server. Ahora lo que yo no quiero es andar pasando el id en cada método (declararlo en la interfaz) y me gustaria que eso se hiciera por abajo del agua.

Leí sobre los interceptors pero creo que no eh encontrado algo acorde a lo que ocupo. Hize un method interceptor que ya logré hacer funcionar, pero aun no eh decifrado como yo desde el cliente puedo modificar la invocación del método remoto para agregarle un parámetro (el id de sessión) y más aun como puedo en el interceptor remoto eliminar este id para que se llame el método como es (no me vaya a lanzar algo como que no existe el método para los parámetros enviados).

Si alguno ha tratado de implementar algo parecido o tiene una idea de como podria implementarse, se lo agradeceria.

sobres.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Como funcionan los interceptors en spring?

El otro problema con la autentificación es que en el cuadro de login se captura una fecha, la cual será la fecha de trabajo durante toda la sessión, inclusive los servicios remotos deben trabajar con esta fecha, por lo que pense en un modelo donde con cada session se cree un row en una tabla de sesiones y que retorne un id al usuario, y que sea éste id el que ande pa alla y pa aca con cada llamada al server. Ahora lo que yo no quiero es andar pasando el id en cada método (declararlo en la interfaz) y me gustaria que eso se hiciera por abajo del agua.

Observación obvia #1: El enfoque pragmático y sencillo de la solución aquí narrada recuerda lo que creo es la manera más utilizada mundialmente para llevar el registro de sesiones entre un cliente y un servidor: el acarreo de un ID de sesión por medio de adjuntar un parámetro/valor o una galleta a la petición GET HTTP. Simple, efectivo, comprobado.

Pregunta tonta # 9865: ¿Cómo podría "adivinar" el servidor la sesión correspondiente si no recibe por medio de un parámetro declarado explícitamente en su interfaz o como parte de los datos serializados en la llamada una manera de identificar?

Mi opinión es que has planteado soluciones sencillas y elegantes y en lo personal no veo nada de malo con pasar el ID de sesión o inclusive hacer que la fecha de trabajo forme parte de los parámetros enviados al servidor. Donde veo lugar para los interceptores sería acaso para agregar automágicamente el ID o fecha de trabajo a cada llamada al servidor. ¿Ya te has dado el tiempo de revisar la documentación de AOP de Spring?
http://static.springframework.org/spring/docs/2.5.x/reference/aop.html

Saludos

Javier Castañón

Como funcionan los interceptors en spring?

Otra cosa que se me acaba de ocurrir es por qué no en lugar de usar interceptores, implementas un especie de pipeline en el cliente para procesar las llamadas al servidor, con una cadena de responsabilidad o algo así.

Claro, si estás buscando los interceptores porque ya tienes un montón de código y en producción, pues ni modo de andar cambiando la implementación, ¿no? Los interceptores son chidos, pero en manos de programadores que no los comprendan bien (en caso de que tu código no sea sólo tu código, sino que otros lo tengan que aumentar y mantener), pues viene problemas porque se corta la pila de llamadas, los bugs se pueden volver difíciles de diagnosticar porque no se sabe dónde quedó la bolita (hice mi tarea maestra, pero un interceptor se la comió y no llegó), etc.

Saludos

Javier Castañón

Imagen de ezamudio

Agrega el ID explícitamente

Creo que lo mejor es que agregues el ID explícitamente como parámetro, porque los interceptores no son la mejor solución para este problema. Si usas Eclipse o NetBeans no va a ser tan complicado porque al cambiar los métodos en cuestión para RMI, pasando el ID de sesión en todos, te van a salir los errores de todas las referencias a métodos que ahora son inválidos. Esto es importante porque además tienes que estar manejando ese ID de la siguiente manera:
- Al hacer login, el server le da un ID al cliente. El cliente debe guardar ese ID para enviarlo en todas las invocaciones posteriores al server.
- Si vas a manejar timeout de sesiones en el server (muy recomendable), entonces debes validar el ID que te llega en cada invocación en el server, y si ya no está en la tabla (porque otro proceso que revisa los timeouts lo borró), rechazar la petición. Para eso sí puede ser útil un interceptor, que valide el ID de sesión y arroje una excepción si ya no existe ese ID en el server.
- Al hacer logout hay que enviar un logout(id) al server para que se borre ese ID y nadie más lo pueda usar.

Como menciona Javier, los interceptores no son la mejor solución para tu problema.

Imagen de Nopalin

pueque si.. pueque no

simon ezamudio eso es lo más claro y entendible, pero no me gusta esa solución. Como bien lo dijo javier, el modelo que plantié es altamente usado y efectivo.

Ahora, como lo dije en el primer post, he utilizado acegi como seguridad en otro proyecto y sinceramente no eh entendido como funciona, solo sé que funciona. en ese modelo yo no tengo que pasar ningun id de session en mis métodos de servicio, lo hace transparente para mí. Algo asi me gustaria pero obviamente implementado por mí. Yo creo que acegi usa interceptos por la forma de operar, pero realmente no estoy seguro y no me he dado el tiempo de buscar.

Como tengo algo de prisa en acabar este proyecto, decidí postear mi duda a ver si me podían hechar la mano, si no pus creó que lo que haré es pasar el id de sessión en cada método.

sobres

Agrega el ID explícitamente

- Si vas a manejar timeout de sesiones en el server (muy recomendable), entonces debes validar el ID que te llega en cada invocación en el server, y si ya no está en la tabla (porque otro proceso que revisa los timeouts lo borró), rechazar la petición. Para eso sí puede ser útil un interceptor, que valide el ID de sesión y arroje una excepción si ya no existe ese ID en el server.

De acuerdo. Y si nos vamos más allá, si la sesión se refiere simplemente a la fecha de trabajo, ¿por qué no utilizar la propia fecha o una cadena como identificador de la sesión? El servidor sería stateless, y sería responsabilidad de cada cliente no hacer tonterías y enviar siempre la misma fecha en todas las llamadas al servidor para una sesión dada.

Saludos

Javier Castañón

Agrega el ID explícitamente

Yo creo que acegi usa interceptos por la forma de operar, pero realmente no estoy seguro y no me he dado el tiempo de buscar.

Imagino que en el código de Acegi hace algo así como
session = httpServletRequest.getSession();

Así que sería "transparente" para el desarrollador, pero debajo, siempre hay un ID. Por eso te mencionaba que si usabas un pipeline, uno de los eslabones en la cadena de responsabilidad es ver el valor de la fecha en la sesión en el cliente y llenar el parámetro resultante que sería enviado al servidor. De esta manera nadie más que dicho eslabón se preocupa por la sesión.

Saludos

Javier Castañón

Imagen de ezamudio

Seguridad

Por seguridad conviene validar el ID de sesión en cada invocación, que debe ser provisto por el servidor. De otra forma, simplemente con interceptar la fecha en que se inicia la sesión ya tienes el ID para hacer cosas en nombre de otro usuario.

Seguridad

Es según su requerimiento, no se distingue entre los usuarios:

El tipo de autentificación es demasiado sencilla, solo se requiere que el password sea válido (ni si quiera un usuario se captura) y siempre se tienen todos los permisos.

De cualquier manera, si ya está implementado lo de la sesión, lo que escribí en mi mensaje anterior no tiene sentido, pueden mandarlo a /dev/null:

pense en un modelo donde con cada session se cree un row en una tabla de sesiones y que retorne un id al usuario, y que sea éste id el que ande pa alla y pa aca con cada llamada al server

doh!

Saludos

Javier Castañón

Imagen de Nopalin

no es web

Estamos hablando de una aplicacion web? Lo que estoy utilizando es rmi para la comunicación cliente-servidor.

Mi server es es una aplicación simple y llana de java, que utiliza spring para inicializar. Con spring utilizo la transaccionalidad de mis servicios usando DataSourceTransactionManager y exporto mis clases de servicio con RmiServiceExporter. Como ven es una app myu simple, y como dije la seguridad es algo no muy importante aqui.

ahora bien, supongamos que yo no hable nada interceptos ni de seguridad ni nada... alguno de ustedes sabe como funciona acegi?

sobres

no es web

Estamos hablando de una aplicacion web? Lo que estoy utilizando es rmi para la comunicación cliente-servidor

Es lo mismo. ¿Cómo pasamos datos entre los espacios de memoria de dos procesos en hosts distintos? Por RPC, RMI o como le queramos llamar, pasando los datos por valor, no por referencia, serializándolos en un extremo y deserializándolos en el otro.

Pero según entiendo lo que planteas es cómo hacerle para no andar pasando el ID de sesión entre el cliente y el servidor, y eso es imposible (la explicación viene más abajo), pues estamos hablando de espacios de memoria remotos. El ID de sesión se debe transmitir al servidor al menos una vez.

Para interceptar las llamadas y verificar la seguridad, Acegi no requiere que le pasemos el ID serializado en una aplicación y por eso se ve lindo y toda la cosa porque corre en el mismo espacio de memoria (llamémoslo sesión o contexto) en el cual se ejecuta el código que intercepta.

Por ejemplo, revisa la clase org.acegisecurity.intercept.AbstractSecurityInterceptor. El primer paso en la inicialización de esta clase es acceder al SecurityContextHolder. Ahí está, ¡esa es la puerta de entrada a la lectura del espacio de memoria compartido! Luego del mismo contexto obtiene una especie de catálogo lista blanca/negra llamado ObjectDefinitionSource, para determinar si debe o no interceptar la llamada. Ahora bien, ¿cómo hacer para que el interceptor se dispare? Pueden usarse al menos dos estrategias: por medio de un framework o instrumentando el código. Con un framework, éste se encarga de llamar en ciertos puntos a tu código, así que tu nomás creas una instancia y el framework en algún momento de su procesamiento la mandará llamar. Instrumentando el código, se usa una herramienta que modifique el bytecode para "inyectar" las llamadas (que es lo que ocurre con AspectJ).

No sé que tanto esfuerzo implique que te fabriques un micro frameworkcito y se lo calces a la aplicación para asegurar que se hacen las llamadas necesarias, o si de plano mejor utilizarías AspectJ . Por eso también había propuesto un pipeline, donde justo antes de realizar la llamada al servidor se inyecta el ID de sesión. El punto importante aquí es que de todas maneras terminas enviando el ID de sesión en cada llamada, por eso decía que era imposible no enviarlo.

Otra alternativa es diseñar una aplicación orientada a la conexión: el cliente negocia una conexión RMI con el servidor, cuando la conexión se establece, el servidor toma un thread de un pool y le asocia una nueva estructura de datos (un Map adjuntado a un ThreadLocal por ejemplo) donde guarda ciertos datos asociados al cliente (una sesión). En nuestro caso la fecha de trabajo puede establecerse como parte del handshake y el servidor utiliza esa misma fecha de trabajo mientras dure la conexión sin volverla a pedir una sola vez más. Si el cliente quiere una nueva fecha de trabajo, cierra la conexión actual, el servidor manda a la basura el Map asociado a la conexión y todas las referencias en él contenidas. Otra opción es complicar un poco el protocolo y permitir una renegociación de la fecha de trabajo. A esto me refería cuando decía que el ID de sesión debía de enviarse al menos una vez

Por lo anterior y considerando que prácticamente no hay requerimientos de seguridad, me había atrevido a sugerir el modo más simple: pasar en todas las llamadas el ID de sesión o inclusive la fecha plana y mantenerlo completamente stateless.

Saludos

Javier Castañón

Imagen de Nopalin

El punto importante aquí es

El punto importante aquí es que de todas maneras terminas enviando el ID de sesión en cada llamada, por eso decía que era imposible no enviarlo.
Claro, tampoco quiero hacer magia. Estoy conciente que en cada llamada el cliente le manda el id de session al server para obtener datos únicos. A lo que me referia era que el id se pasará en una forma similar a como lo hace acegi, que yo no tenga que declarar ese id en cada método de mis servicios remotos, algo como:

public interface Servicio{
   void creaLeon(int sessionId, LeonDto dto);
   //....
}

Ya con la explicación que me diste sobre el funcionamiento de acegi, creo entender un poco mas pero si es medio tardado implementar algo así, asi que como la seguridad es mínima, casi casi que inexistente, me atreví a hacer algo feo, mejor paso la fecha de trabajo en cada método y así me evito adar manteniendo una tabla con las session y creando un thread para limpiarla cuando pase el timeout.

sobres gracias por tu ayuda hombre.

Advertencia para los lectores

Claro, tampoco quiero hacer magia.

Ja, ja, ja, queda clarísimo a partir del momento que decidiste ser pragmático y no meterle 40 frameworks a la aplicación. ;-)

me atreví a hacer algo feo, mejor paso la fecha de trabajo en cada método y así me evito adar manteniendo una tabla con las session y creando un thread para limpiarla cuando pase el timeout.

¡Qué asquerosidad! ... mmmhhhh... yo mismo estuve de acuerdo con eso antes... en fin. Dado que en esta comunidad he visto mucha gente que está aprendiendo a programar, una advertencia respecto a esta implementación: como a veces le decimos a los niños: esto puede verse también como un ejemplo de lo que NO se debe hacer. Al rato pueden pedir seguridad y deberíamos implementar un ID de sesión como decía ezamudio, creado y verificado por el servidor, etc.

El señor Nopalin sabe lo que está haciendo, conoce sus requerimientos y con plena conciencia está tomando estas decisiones de diseño. Niños, no lo hagan solos en casa.

Caveat emptor.

Javier Castañón

Imagen de benek

Jajajaja, sobre advertencia

Jajajaja, sobre advertencia no hay engaño!

Suerte con aquello Nopalín.

--
Javier Benek

Imagen de Nopalin

simon.... (tas camello trepate a la joroba)

jajajaja echo chi...

bueno, estoy conciente de que si me llegan a pedir seguridad, tendré que implementar algo, pero vaya como ya existe acegi, no creo que tarde mucho en hacerlo jeje... ademas cobraría extra por eso :D

sobres!