Manejo de cabeceras SOAP en WebService

Fecha: 24/Feb/2005 (23/2/2005)
Autor: Matias Iacono matias.iacono@gmail.com

 


Cuando desarrollamos cualquier aplicación que manipule información, una de las principales preocupaciones es la seguridad. Seguridad que usuarios no autorizados puedan ingresar a nuestro sistema y a nuestros datos, pudiendo eliminar datos a su antojo.

En el caso del desarrollo de servicios web este tema no deja de ser importante. Mas si pensamos que el nuevo esquema de desarrollo impulsado por .Net, coloca a los servicios web como parte central en cualquier aplicación, debido a que sirven como nexo entre una aplicación y otra, o, entre una aplicación y sus datos.

En la mayoría de los casos, cuando necesitamos agregar algún tipo de seguridad a nuestros servicios web, recurrimos a una solución muy simple. A nuestro método web público, le agregamos dos variables, una para el nombre de usuario y otra para la contraseña, las cuales usaremos para compararlas con alguna fuente de datos y así podremos comprobar que, aquella información es válida y garantizar la autenticidad del usuario.

Esta es una técnica comúnmente usada, pero, en nuestro caso puede presentar algunos problemas.

Supongamos que hemos creado un método web, y este método necesita grandes cantidades de parámetros de entrada. Además, tendremos que agregarle parámetros para capturar un nombre de usuario y una contraseña. Dentro de un par de semanas deberemos agregarle otros parámetros, que a su vez, nos servirán para hacer otro tipo de validación. Si tenemos grandes cantidades de métodos, el mantenimiento de estos se puede convertir en un infierno.

Por eso, necesitamos algo que nos ayude a hacer esta tarea mas fácilmente. Ese algo es SOAP.

¿Qué es SOAP?.

En pocas palabras, un estándar que nos dice la forma de empaquetar los mensajes. Esto es, la forma en como se estructuraran los datos.

En definitiva, SOAP (Simple Object Access Protocol), no es mas que un documento XML enviado y recibido por medio de HTTP con una estructura especifica.

Dentro de las partes que componen un mensaje SOAP, podemos destacar su cabecera, la cual usaremos para simplificar la aplicación de autentificación de usuarios en nuestros servicios web.

¿Para que nos servirá la cabecera SOAP?.

Uno de los problemas que vimos al principio fue el del mantenimiento de nuestros servicios web. Y como nos puede afectar el tener que agregar o quitar parámetros a cada uno de nuestros métodos web.

Entonces, busquemos una forma que, con solo enviar una vez esta información, nos sirva para todos los métodos web. Es ahí donde entra en juego la cabecera SOAP.

Cada vez que invocamos un metodo web, además de los parámetros que este pueda aceptar, siempre es enviada una cabecera, la que puede contener información. Esta cabecera esta ahí, aunque nosotros, por no usarla, no la veamos.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <SimpleMethod xmlns="http://tempuri.org/SoapWS/Service1">
      <UserName>string</UserName>
      <PassWord>string</PassWord>
    </SimpleMethod>
  </soap:Body>
</soap:Envelope>
Podemos ver, en este caso, el esquema habitual para enviar un mensaje a un método web.

Veamos que pasa cuando agregamos la cabecera SOAP a este mensaje.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <LoginInfo xmlns="http://tempuri.org/SoapWS/Service1">
      <UserName>string</UserName>
      <PassWord>string</PassWord>
    </LoginInfo>
  </soap:Header>
  <soap:Body>
    <SoapSecure xmlns="http://tempuri.org/SoapWS/Service1">
    </SoapSecure>
  </soap:Body>
</soap:Envelope>
La diferencia entre uno y otro, es, básicamente, que nuestros parámetros están ahora, separados de los que nos servirán para hacer nuestra autentificación.

Pudiendo agregarle esta cabecera a cualquier método web, con la sola referencia de la misma.

La implementación.

Hasta el momento todo parece muy simple, pero que tan complicado puede ser crear esta cabecera.

Lo primero que deberemos hacer es importar el espacio de nombre que maneja esta cabecera y crear una clase, la cual contendrá las variables que harán referencia al nombre de usuario y contraseña. Esta clase, puede estar dentro del mismo servicio web.

Para el ejemplo la llamaremos LoginInfo.

Imports System.Web.Services.Protocols

Public Class LoginInfo
    Inherits SoapHeader

    Public UserName As String
    Public PassWord As String
End Class

Esta clase, heredará la cabecera SOAP mediante SOAPHEADER. También debemos notar que esta clase debe ser publica. Esto último nos servirá para poder crear una instancia de la misma fuera del ámbito del servicio web, como por ejemplo, alguna aplicación .Net que requiera conectarse a él.

Luego, en la clase principal del servicio web, creamos una variable que manejara esta clase, en nuestro caso, LoginCredential.

Public LoginCredential As LoginInfo

Por último, solo nos queda crear un método público que implementará esta cabecera.

<WebMethod(), SoapHeader("LoginCredential", direction:=SoapHeaderDirection.InOut)> _
    Public Function SoapSecure() As String
        If LoginCredential Is Nothing Then
            Return "Usuario Incorrecto"
        Else
            Return "Usuario " & LoginCredential.UserName
        End If
    End Function
A diferencia de los métodos comunes, este contendrá algunas directivas adicionales dentro del tag que conforman la declaración del método.

Deberemos adicionarle la directiva SOAPHEADER, la cual hace referencia, justamente, a la cabecera SOAP. Como primer parámetro encontramos el nombre del objeto que manejará esta cabecera. Notemos que es el mismo nombre que usamos para la variable que maneja nuestra clase LoginInfo.

Luego, la implementación de los resultados. El descriptor DIRECTION nos dice hasta donde llegaran los datos. Para entenderlo mejor, en el caso de seleccionar InOut, hacemos denotar que la información llegara al servicio web y además, los datos resultantes serán enviados, sí o sí, al cliente. Podríamos haber elegido la opción FAULT, la cual enviaría información al cliente, solo si el método web hubiera arrojado un error.

Por último, para conocer si el usuario envió en primera instancia, las credenciales, solo deberemos verificar si el objeto LoginCredential fue creado. En caso contrario, podríamos retornar un mensaje de error.

Accediendo al servicio web.

Finalmente, necesitamos saber como acceder a este método, que ahora posee nuevas características.

Por un lado, creamos la referencia web en nuestro proyecto, la cual se crea de la forma tradicional.

A continuación creamos, una nueva instancia de nuestro método, con la única diferencia que, antes de enviarle los parámetros deberemos instanciar y manipular la cabecera por medio de la clase pública LoginInfo de nuestro servicio web.

Dim LoginSoap As New MySoap.LoginInfo
Dim WSService As New MySoap.Service1
Enviamos el nombre de usuario y la contraseña a nuestra cabecera.
LoginSoap.UserName = "usuario1"
LoginSoap.PassWord = "123"
Y, por ultimo, la asignamos al servicio web en si.
WSService.LoginInfoValue = LoginSoap
Desde este momento, podremos usar cualquier método, con la seguridad que la cabecera estará incluida automáticamente.

En resumen.

Simplemente, lo que hemos hecho fue separar la información que sería obligatoria al tratar de ejecutar cualquier método web de nuestro servicio web, ahorrándonos código en el momento que tengamos que hacer mantenimiento a nuestros métodos.

Tenemos que tener en cuenta, que el uso en si de SOAP no da garantías de seguridad, ya que los datos siguen siendo enviados en texto plano, pero, podríamos, a esta modalidad de trabajo, agregarles otras formas de seguridad, como encriptación, SSL, etc.


Espacios de nombres usados en el código de este artículo:

System.Web.Services.Protocols

 


ir al índice