Artículo original publicado el sábado, 2 de abril de 2011

Solo la línea de asunto de esta entrada ya es un trabalenguas, si se trata de alguna indicación de lo que vamos a tratar hoy.  En esta entrada se va a tratar un problema que me ha molestado a mí y a otros durante mucho tiempo.  Hace poco empecé a indagar sobre ello y casualmente recibí un mensaje de correo electrónico de alguien que acababa de recibir información acerca de cómo resolver el problema.

 

Solamente voy a tratar las partes más comunes de este escenario; se pueden agregar varias vueltas de tuerca.   El problema consiste en recuperar datos desde un sitio de SharePoint que usa varios proveedores de autenticación. En este escenario, se asume que unas son notificaciones de Windows y otras son cualquier otra (podría ser autenticación basada en formularios o autenticación de SAML).  Con frecuencia le interesará recuperar datos de este tipo de sitio mediante el modelo de objetos cliente o servicios web de SharePoint, pero usando la autenticación de Windows para hacerlo.  El problema hasta este punto siempre ha sido que, aun cuando se establezcan las credenciales de Windows en la solicitud, se tiene acceso denegado al solicitar los datos.

 

La solución subyacente a este problema es que si se desea obtener acceso mediante programación a un sitio de SharePoint que usa varios proveedores de autenticación con un conjunto de credenciales de Windows, se debe agregar un encabezado adicional a la solicitud.  El nombre del encabezado debe ser X-FORMS_BASED_AUTH_ACCEPTED y el valor debe ser "f".  La adición de este encabezado puede ser no tan sencilla para estos dos escenarios comunes, por lo que en el resto de la entrada de blog voy a explicar cómo hacerlo con código de ejemplo.

 

Si va a usar el modelo de objetos de cliente, deberá agregar un controlador de eventos para el evento ExecutingWebRequest.  Este es un ejemplo del código y de su aspecto:

 

//create the client context

ClientContext ctx = new ClientContext(MixedUrlTxt.Text);

 

//configure the handler that will add the header

ctx.ExecutingWebRequest +=

new EventHandler<WebRequestEventArgs>(ctx_MixedAuthRequest);

 

//set windows creds

ctx.AuthenticationMode = ClientAuthenticationMode.Default;

ctx.Credentials = System.Net.CredentialCache.DefaultCredentials;

 

//get the web

Web w = ctx.Web;

 

//LOAD LISTS WITH ALL PROPERTIES

var lists = ctx.LoadQuery(w.Lists);

 

//execute the query

ctx.ExecuteQuery();

 

//enumerate the results

foreach (List theList in lists)

{

//do something with each list

}

 

Y aquí es donde la magia sucede:

 

void ctx_MixedAuthRequest(object sender, WebRequestEventArgs e)

{

try

{

              //add the header that tells SharePoint to use Windows Auth

              e.WebRequestExecutor.RequestHeaders.Add(

"X-FORMS_BASED_AUTH_ACCEPTED", "f");

       }

       catch (Exception ex)

       {

              MessageBox.Show("Error setting auth header: " + ex.Message);

       }

}

 

Eso es todo; este es bastante fácil y creo que se explica por sí solo.  Hacer lo mismo con una referencia al servicio web estándar es un poco diferente.  Para empezar, vamos a examinar el proceso de agregar una referencia web de estilo estándar en un sitio de SharePoint a un proyecto en Visual Studio 2010:

1.       Haga clic con el botón secundario en el nodo de referencia de servicio y seleccione Agregar referencia de servicio.

2.       Haga clic en el botón Opciones avanzadas en la parte inferior del cuadro de diálogo.

3.       Haga clic en el botón Agregar referencia web en la parte inferior del siguiente cuadro de diálogo.

4.       Escriba la dirección URL del servicio web que desee usar en el cuadro de edición de la URL. Por ejemplo, para agregar una referencia al servicio web de listas en el sitio en http://foo, se escribiría lo siguiente en la dirección URL:  https://foo/_vti_bin/lists.asmx.

5.       Presione Entrar o haga clic en el botón de flecha verde para buscar la referencia al servicio web. A continuación, escriba un nombre para la referencia al servicio web en el cuadro de edición del nombre de referencia web y haga clic en el botón Agregar referencia.

 

Las clases de referencia y proxy para la referencia se deben crear ahora, pero se debe agregar una clase parcial adicional a fin de agregar el encabezado a la solicitud de servicio web.  Empiece agregando una nueva clase al proyecto y asígnele el nombre que desee.  Puesto que solo desea agregar algún comportamiento adicional (mediante la adición de un encabezado a la solicitud), tal vez desee convertirla en una clase parcial.  Esto significa que debe copiar tanto el espacio de nombres como el nombre de clase usado en el proxy creado para la referencia web. A continuación, se indican los pasos para hacerlo:

 

1.       Haga clic en el botón Mostrar todos los archivos en la ventana del Explorador de soluciones de Visual Studio.

2.       Haga clic en el signo más situado junto a la referencia al servicio web para expandirla.

3.       Haga clic en el signo más junto al archivo Reference.map para expandirlo.

4.       Haga doble clic en el archivo Reference.cs para abrirlo.

5.       Copie el espacio de nombres y péguelo en la clase.

6.       Copie el nombre de clase, incluida la herencia, y péguelo en la clase como el nombre de clase.  La clase de referencia al servicio web ya es una clase parcial, por lo que no debe hacerse ningún cambio ahí.

 

Este es un ejemplo del aspecto que tenía la clase Reference.cs en mi referencia al servicio web:

namespace ClientOmAuth.listsWS {

    using System;

    using System.Web.Services;

    using System.Diagnostics;

    using System.Web.Services.Protocols;

    using System.ComponentModel;

    using System.Xml.Serialization;

   

   

    /// <remarks/>

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.ComponentModel.DesignerCategoryAttribute("code")]

    [System.Web.Services.WebServiceBindingAttribute(Name="ListsSoap", Namespace="http://schemas.microsoft.com/sharepoint/soap/")]

    public partial class Lists : System.Web.Services.Protocols.SoapHttpClientProtocol {

 

Y esto es lo que pegué en la clase que creé:

namespace ClientOmAuth.listsWS

{

       public partial class Lists : System.Web.Services.Protocols.SoapHttpClientProtocol

       {

       }

}

 

Por lo tanto, observará cómo coinciden tanto el espacio de nombres como los nombres de clase, y que ambas clases heredan del mismo tipo base.

 

Ahora tiene que invalidar el método GetWebRequest para que pueda agregar el encabezado.   Para ello, simplemente agregue este código a la clase parcial:

 

protected override System.Net.WebRequest GetWebRequest(Uri uri)

{

       System.Net.WebRequest wr = null;

 

       try

       {

              wr = base.GetWebRequest(uri);

              wr.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");

       }

       catch (Exception ex)

       {

              //some error handling here

       }

                     

       return wr;

}

 

 

La codificación para recuperar los datos a través del servicio web es ahora exactamente la misma que se usaría con cualquier sitio de SharePoint con autenticación de Windows:

 

//create the web service proxy and configure it use my Windows credentials

listsWS.Lists lws = new listsWS.Lists();

lws.UseDefaultCredentials = true;

 

//get the collection of lists

XmlNode xLists = lws.GetListCollection();

 

//enumerate results

foreach (XmlNode xList in xLists.ChildNodes)

{

//do something with each List

}

 

Eso es todo.  Puede aplicar esta información para recuperar datos por medio de REST, así como mediante las técnicas que se describen aquí:  http://blogs.technet.com/b/speschka/archive/2010/09/25/retrieving-rest-data-in-a-claims-based-auth-site-in-sharepoint-2010.aspx.

 

Esta entrada de blog es una traducción. Puede consultar el artículo original en Retrieving Data from a Multi Auth Site Using the Client OM and Web Services in SharePoint 2010