Previsão: nublado

Mashup de serviços em nuvem

Joseph Fultz

Baixar o código de exemplo

Até agora, falei sobre soluções que usam o Microsoft Windows Azure ou o SQL Azure para incrementar a respectiva arquitetura. Neste mês, vou analisar como é possível combinar vários serviços em nuvem em um único aplicativo. Meu exemplo combinará Windows Azure, Controle de Acesso do Windows Azure AppFabric, Bing Maps e Facebook para mostrar como compor serviços em nuvem.

Para aqueles que ficam um pouco confusos quando pensam em identidade federada ou no valor real das redes sociais, gostaria de apresentar o Marcelus. É um amigo que tem uma empresa de limpeza residencial e comercial. Assim como o meu pai em seus negócios pessoais e profissionais, ele conhece alguém que pode fazer ou conseguir praticamente qualquer coisa que você precise, geralmente sob alguma forma de permuta. Alguns podem reconhecer isso como a boa e velha rede de ‘amigos’, mas olho para o Marcelus e vejo um exemplo vivo e verdadeiro do serviço Controle de Acesso do Windows Azure AppFabric (ou ACS) combinado a uma poderosa rede social. Na vida real, posso aproveitar o Marcelus e outros parecidos com ele para me ajudar.

Porém, no mundo virtual, quando uso uma série de serviços em nuvem, geralmente eles precisam saber quem eu sou antes de me autorizarem a acessar suas funcionalidades. Como não posso programar o Marcelus para fornecer páginas da Web, vou usar os serviços em nuvem da Figura 1 para propiciar a funcionalidade.

Figura 1 Serviços em nuvem e suas funcionalidades

Serviço Funcionalidade
Windows Azure Hospedar meu site e fornecer páginas
Controle de Acesso do AppFabric Gerenciar e negociar a autenticação entre o meu site e o Facebook
Facebook Autenticar usuários e fornecer serviços de rede social
Bing Maps Visualizar as cidades natal de amigos

Neste cenário, a navegação até a home page do meu site será autenticada pelo Facebook, e as declarações serão devolvidas ao meu site. O site então obterá os amigos desse usuário do Facebook e, em seguida, extrairá as informações de um determinado amigo. Se o amigo selecionado tem uma cidade natal especificada, o usuário pode clicar no nome da cidade natal e o Bing Map a mostrará.

Configurando a autenticação entre serviços

A edição de dezembro de 2010 da MSDN Magazine trouxe um excelente artigo de visão geral do ACS, disponível em msdn.microsoft.com/magazine/gg490345. Abordarei os pontos específicos que preciso fazer para federar o meu site ao Facebook. Para que isso funcione corretamente, vou usar o Labs do AppFabric, que é a visualização do desenvolvedor do Windows Azure AppFabric. Além disso, estou usando o Windows Azure SDK 1.3 e instalei o Windows Identity Foundation SDK 4.0. Para começar, acessei portal.appfabriclabs.com e me registrei. Depois de obter acesso ao ACS, segui a primeira parte das instruções disponíveis na página do CodePlex sobre exemplos e documentação do ACS (Labs) (bit.ly/fuxkbl) para configurar o namespace de serviço. O próximo objetivo era configurar o Facebook como um provedor de identidade, mas para isso primeiro tive de criar um aplicativo para Facebook (veja as orientações em bit.ly/e9yE3I), com resultados em um resumo como o ilustrado na Figura 2.

Figura 2 Resumo da configuração do aplicativo para Facebook

Esta página de resumo é importante porque terei de usar informações dela na configuração do Facebook como provedor de identidade no ACS. Especificamente, precisarei da identificação do aplicativo e do segredo do aplicativo, como podemos ver nas informações de configuração do ACS mostradas na Figura 3.

Figura 3 Configuração do Facebook como provedor de identidade do ACS

Observe que adicionei friends_hometown à caixa de texto de permissões do aplicativo. Precisarei desse endereço para mapeá-lo e, sem especificá-lo aqui, por padrão ele não seria retornado. Se eu quisesse que as chamadas da API gráfica retornassem outros dados sobre o usuário, teria de procurar no site para desenvolvedores do Facebook (bit.ly/c8UoAA) e incluir o item na lista de permissões do aplicativo.

Algo que vale a pena mencionar quando se trabalha com o ACS: você deve especificar as partes confiáveis que usarão cada provedor de identidade. Se o meu site existe em jofultz.cloudapp.net, será especificado como parte confiável na configuração do provedor de identidade. Isso também é válido para o meu host local. Assim, caso eu não queira recorrer à nuvem para testá-lo, precisarei configurar uma parte confiável de host local e selecioná-la, como ilustrado na Figura 4.

Figura 4 Configuração do Facebook como provedor de identidade do ACS: partes confiáveis

A Figura 3 e a Figura 4 estão na mesma página de configuração do provedor de identidade. Pelo mesmo token, se ele só estivesse configurado para host local e, depois, se eu tivesse tentado autenticar do meu site, não teria funcionado. Posso criar uma página de logon personalizada, e há orientação e um exemplo de como fazê-lo na seção de integração de aplicativos do site de gerenciamento do ACS. Neste exemplo, vou usar somente a página padrão hospedada pelo ACS.

Até agora, configurei o ACS e o meu aplicativo para Facebook para fazer com que eles conversem quando chamados. A próxima etapa é configurar esse provedor de identidade para o meu site como um meio de autenticação. A maneira mais fácil de fazer isso é instalar o Windows Identity Foundation SDK 4.0, disponível em bit.ly/ew6K5z. Uma vez instalado, haverá uma opção no menu de atalho para adicionar referência STS, como ilustrado na Figura 5.

Figura 5 Opção de menu para adicionar referência STS

No meu exemplo, usei um site ASP.NET padrão criado no Visual Studio selecionando um novo projeto Web Role (Função Web). Uma vez criado o projeto, clico com o botão direito do mouse no site e começo a executar as etapas do assistente. Configurarei o site para usar um Serviço de Token de Segurança (STS) existente escolhendo essa opção no assistente e fornecendo um caminho para os metadados de Web Services Federation. Assim, o caminho do meu namespace de controle de acesso é:

jofultz.accesscontrol.appfabriclabs.com/

    FederationMetadata/2007-06/

    FederationMetadata.xml

Usando essa informação, o assistente adicionará a seção de configuração <microsoft.identityModel/> à configuração do site. Depois que isso for feito, adicione <httpRuntime requestValidationMode=“2.0” /> sob o elemento <system.web/>. Considerando que especifiquei host local como parte confiável, eu deveria poder executar o aplicativo e, na inicialização, ver uma página de logon hospedada pelo ACS que apresentará o Facebook — ou o Windows Live ou o Google, se assim estiver configurado. O elemento microsoft.identityModel depende da existência do assembly Microsoft.Identity, por isso você deve definir essa referência de DLL no site como Copiar Sempre. Do contrário, uma vez enviado para o Windows Azure, ele não terá a DLL e haverá falha na execução do site. Mencionando minha afirmação anterior sobre precisar ter uma configuração para host local e o site hospedado pelo Windows Azure, é necessário mais um pouco de configuração depois que o assistente é concluído. Assim, se o assistente foi configurado com o caminho do host local, será preciso adicionar um caminho para o site do Windows Azure ao elemento <audienceUris>, conforme mostrado aqui:

<microsoft.identityModel>

  <service>

    <audienceUris>

      <add value="http://jofultz.cloudapp.net/" />

      <add value="http://localhost:81/" />

    </audienceUris>

Além disso, o atributo de realm do elemento wsFederation na configuração deverá refletir a localização atual do tempo de execução desejado. Assim, quando implantado no Windows Azure, ele se parece com o seguinte:

<federatedAuthentication>

  <wsFederation passiveRedirectEnabled="true" issuer=

   "https://jofultz.accesscontrol.appfabriclabs.com/v2/wsfederation" 

   realm="http://jofultz.cloudapp.net/" requireHttps="false" />

  <cookieHandler requireSsl="false" />

</federatedAuthentication>

No entanto, se eu quiser depurá-lo para que funcione corretamente no tempo de execução no meu host local (para depuração local), alterarei o realm para representar onde o site está hospedado localmente, como o seguinte:

<federatedAuthentication>

  <wsFederation passiveRedirectEnabled="true" 

   issuer="https://jofultz.accesscontrol.

   appfabriclabs.com/v2/wsfederation" 

   realm="http://localhost:81/" 

   requireHttps="false" />

  <cookieHandler requireSsl="false" />

</federatedAuthentication>

Estando tudo devidamente configurado, devo poder executar o site e, ao tentar acessar a página padrão, serei redirecionado para a página de logon hospedada pelo ACS, onde posso escolher o Facebook como provedor de identidade. Quando clico em Facebook, sou levado à página de logon do Facebook para ser autenticado (veja a Figura 6).

Figura 6 Logon no Facebook

Como não usei meu aplicativo antes, o Facebook mostra a caixa de diálogo Solicitar Permissão do aplicativo, como podemos ver na Figura 7.

Figura 7 Solicitação de permissão do aplicativo

Não querendo ficar de fora do grupo dos que usam esse fantástico aplicativo, clico rapidamente em Permitir, depois em Facebook, no ACS e nas informações de intercâmbio de aplicativo (via redirecionamentos do navegador) e, por fim, sou redirecionado ao meu aplicativo. Nesse ponto, vejo somente uma página vazia, mas ele sabe quem eu sou, e recebo a mensagem “Bem-vindo, Joseph Fultz” no canto superior direito da página.

API gráfica do Facebook

No caso do meu aplicativo, preciso extrair os amigos que formam a minha rede social e, depois, recuperar informações sobre eles. O Facebook disponibilizou a API gráfica para que os desenvolvedores possam fazer isso. Ela é muito bem documentada e, o melhor de tudo, é uma implementação clara e simples, o que facilita o entendimento e o uso. Para fazer as solicitações, precisarei de um token de acesso. Felizmente, ele foi retransmitido nas declarações e, com a ajuda do Windows Identity Foundation SDK, as declarações foram colocadas na identidade principal. As declarações são parecidas com o seguinte:

https://schemas.xmlsoap.org/ws/2005/05/  

    identity/claims/nameidentifier

  https://schemas.microsoft.com/ws/2008/06/

    identity/claims/expiration

  https://schemas.xmlsoap.org/ws/2005/05/

    identity/claims/emailaddress

  https://schemas.xmlsoap.org/ws/2005/05/

    identity/claims/name 

  http://www.facebook.com/claims/AccessToken

  https://schemas.microsoft.com/

    accesscontrolservice/2010/07/claims/

    identityprovider

O que eu quero mesmo com isso é a última parte do nome completo (por exemplo, identificador de nome, validade etc.) e o valor relacionado. Por isso, crio o método ParseClaims para separar as declarações, que são colocadas junto com seus valores em uma tabela de hash para uso posterior, e chamo esse método no evento de carregamento de página: 

protected void ParseClaims()

{

  string username = default(string);

  username = Page.User.Identity.Name;



  IClaimsPrincipal Principal = (IClaimsPrincipal) Thread.CurrentPrincipal;

  IClaimsIdentity Identity = (IClaimsIdentity) Principal.Identity;



  foreach (Claim claim in Identity.Claims)

  {

    string[] ParsedClaimType = claim.ClaimType.Split('/');

    string ClaimKey = ParsedClaimType[ParsedClaimType.Length - 1];



    _Claims.Add(ClaimKey, claim.Value);

  }             

}

Crio uma classe FBHelper onde criarei os métodos para acessar as informações desejadas do Facebook. Para começar, crio um método que ajuda a fazer todas as solicitações necessárias. Farei cada solicitação usando o objeto WebClient e analisarei a resposta com JavaScript Serializer:

public static Hashtable MakeFBRequest(string RequestUrl)

{

  Hashtable ResponseValues = default(Hashtable);



  WebClient WC = new WebClient();

  Uri uri = new Uri(String.Format(RequestUrl, fbAccessToken));

           

  string WCResponse = WC.DownloadString(uri);

  JavaScriptSerializer JSS = new JavaScriptSerializer();

  ResponseValues = JSS.Deserialize<Hashtable>(WCResponse);



  return ResponseValues;

}

Como podemos ver neste trecho de código, cada solicitação deverá ter o token de acesso que foi retransmitido nas declarações. Com o meu método de solicitação reutilizável, crio um método para extrair meus amigos e analisá-los em uma tabela de hash contendo todos os respectivos nomes e identificações no Facebook:

public static Hashtable GetFBFriends(string AccessToken)

{

  Hashtable FinalListOfFriends = new Hashtable();

  Hashtable FriendsResponse = MakeFBRequest(_fbFriendsListQuery, AccessToken);

  object[] friends = (object[])FriendsResponse["data"];



  for (int idx = 0; idx < friends.Length;idx++ )

  {

    Dictionary<string, object> FriendEntry = 

      (Dictionary<string, object>)friends[idx];

    FinalListOfFriends.Add(FriendEntry["id"], FriendEntry["name"]);

  }

  return FinalListOfFriends;

}

A desserialização da resposta da lista de amigos resulta em uma estrutura aninhada de Hashtable->Hashtable->Dictionary. Por isso, tenho um pouco de trabalho pela frente para obter as informações e colocá-las na minha tabela de hash. Depois que isso é implementado, alterno para a página default.aspx, adiciono ListBox, escrevo um código pequeno para obter os amigos e vinculo o resultado ao novo ListBox:

protected void GetFriends()

  {

    _Friends = FBHelper.GetFBFriends((string)_

      Claims["AccessToken"]);

    this.ListBox1.DataSource = _Friends;

    ListBox1.DataTextField = "value";

    ListBox1.DataValueField = "key";

    ListBox1.DataBind();

  }

Se eu executar o aplicativo nesse momento, depois de ser autenticado, verei uma lista de todos os meus amigos do Facebook. Mas, espere — tem mais! Preciso obter as informações disponíveis de qualquer amigo selecionado para poder usar isso e mostrar minha cidade natal em um mapa. Voltando à classe FBHelper, adiciono um método simples que pegará o token de acesso e a identificação do amigo selecionado:

public static Hashtable GetFBFriendInfo(string AccessToken, string ID)

{

  Hashtable FriendInfo = 

    MakeFBRequest(String.Format(_fbFriendInfoQuery, ID) + 

    "?access_token={0}", AccessToken);

  return FriendInfo;

}

Observe que, nos dois métodos auxiliares do Facebook que criei, faço referência a uma cadeia de caracteres constante que contém a consulta da API gráfica necessária:

public const string _fbFriendsListQuery =   

  "https://graph.facebook.com/me/friends?access_token={0}"; 

public const string _fbFriendInfoQuery = "https://graph.facebook.com/{0}/";

Com o meu último método Facebook funcionando, adicionarei um GridView à página e o configurarei para vincular a uma tabela de hash; em seguida, no code-behind do método SelectedIndexChanged de ListBox, o vincularei à Hashtable retornada do método GetFBFriendInfo, como ilustrado na Figura 8.

Figura 8 Adicionando um GridView

protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)

{

  Debug.WriteLine(ListBox1.SelectedValue.ToString());

  Hashtable FriendInfo = 

    FBHelper.GetFBFriendInfo((string)_Claims["AccessToken"],  

    ListBox1.SelectedValue.ToString());

  GridView1.DataSource = FriendInfo;

  GridView1.DataBind();

  try

  {

    Dictionary<string, object> HometownDict = 

      (Dictionary<string, object>) FriendInfo["hometown"];

      _Hometown = HometownDict["name"].ToString();

  }

  catch (Exception ex)

  {

    _Hometown = "";//Not Specified";

  }

}

Agora que tenho meus amigos e suas informações do Facebook, vou mostrar suas cidades natal em um mapa.

Não existe lugar como a nossa casa

Para aqueles meus amigos que especificaram sua cidade natal, quero poder clicar no nome da cidade e fazer com que ela seja mostrada no mapa. A primeira etapa é adicionar o mapa à página. Esta é uma tarefa muito simples e, para isso, o Bing oferece um ótimo SDK interativo que demonstrará a funcionalidade e, depois, permitirá que você veja e copie o código-fonte. Ele pode ser encontrado em microsoft.com/maps/isdk/ajax/. Para a página default.aspx, adiciono um div para conter o mapa, assim:

    <div  id="myMap" style="position:relative; width:400px; height:400px;" ></div>

Entretanto, para colocar o mapa ali, adiciono referência de script e um pouco de script à página SiteMaster:

    <script type="text/javascript" src="http://ecn.dev.virtualearth.net/
    
      mapcontrol/mapcontrol.ashx?v=6.2"></script>      
    
      <script type="text/javascript">
    
        var map = null;
    
        function GetMap() {
    
          map = new VEMap('myMap');
    
          map.LoadMap();
    
        }
    
      </script>

Com isso funcionando, quando eu abrir a página, será exibido um mapa na posição padrão — mas quero que ele mostre a cidade natal do meu amigo quando eu selecioná-la. Durante o evento SelectedIndexChanged discutido anteriormente, também associei um rótulo da página ao nome e adicionei um evento de clique no lado do cliente para que o mapa encontre uma localização com base no valor do rótulo:

    onclick="map.Find(null, hometown.innerText, 
    
        null, null, null, null, true, null, true); 
    
        map.SetZoomLevel(6);"

Na chamada de map.Find, a maioria dos parâmetros à direita poderiam ser ignorados, se desejado. A referência do método Find está disponível em msdn.microsoft.com/library/bb429645. Isso é tudo o que precisamos para mostrar e interagir com o mapa neste exemplo simples. Agora estou pronto para executá-lo em toda a sua glória.

Se configurei identityModel corretamente para funcionar com o meu host local como mencionado antes, posso pressionar F5 e executá-lo localmente na depuração. Então, pressiono F5, vejo uma janela do navegador aparecer e nela são exibidas minhas opções de logon. Escolho Facebook e sou levado à página de logon mostrada na Figura 6. Uma vez conectado, sou levado de volta à página default.aspx, que agora exibe meus amigos e um mapa padrão, como o mostrado na Figura 9.

Figura 9 Home page de demonstração

Em seguida, percorro os meus amigos e clico em um deles. Obtenho as informações que estão disponíveis para mim com base nas configurações de segurança e nas permissões de aplicativo que solicitei quando configurei o provedor de identidade, conforme ilustrado na Figura 2. Depois, clico no nome da cidade natal localizado acima do mapa e o mapa vai para o centro na cidade natal, como podemos ver na Figura 10.

Figura 10 Cidade natal no Bing Maps

Considerações finais

Espero ter expressado claramente como reunir vários aspectos da plataforma Windows Azure, do Bing Maps e do Facebook — e que tenha conseguido mostrar como isso é fácil. Usando o ACS, pude criar um aplicativo de exemplo a partir de uma composição da tecnologia de nuvem. Com um pouco mais de trabalho, é muito fácil vincular o seu próprio serviço de identidade, que atenderá conforme necessário. A beleza dessa federação de identidade é que o uso do Windows Azure permite desenvolver e incorporar serviços de outros fornecedores e outras plataformas — em vez de limitar você a uma única opção de provedor e aos serviços desse provedor ou de ter que descobrir um método de integração de baixa fidelidade. Há poder na plataforma Microsoft Windows Azure, e parte desse poder está na facilidade de combiná-la a outros serviços em nuvem.

Joseph Fultz é arquiteto do Microsoft Technology Center em Dallas, onde trabalha com clientes empresariais e ISVs desenvolvendo e criando protótipos de soluções de software que atendam a demandas dos negócios e do mercado. Ele fez palestras em eventos como o Tech Ed e eventos de treinamento interno semelhantes.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Steve Linehan