CrossDomain Witam ! Dziś postanowiłem dokładnie wyedukować się w tematyce komunikacji sieciowej za pomocą Silverlight.  Choć ta tematyka jest mi znana, to jednak niektórych rzeczy nie próbowałem w praktyce. Cóż to za aplikacja, która czegoś nie pobiera i nie wyświetla na swój własny sposób. Spis treści jest następujący:

Sieć i komunikacja w Silverlight -01- Ograniczenia między domenowy dostęp

Aplikacja Silverlight wykonuje się wewnątrz przeglądarki, nawet będąc w trybie poza nią wykonuje się  oddzielnie (SandBox). Dlatego też trzeba zrobić to inaczej, niż np. w ASP.NET ponieważ jest on technologią wykonującą się po stronie serwera więc może otrzymywać bezpośrednio  dane . Czyli nasza aplikacja w Silverlight nie ma bezpośredniego dostępu do bazy danych bez użycia jakiegoś łącznika, który najczęściej  nazywamy usługą sieciową (web service).

Między domenowy dostęp - Client acces policy

Ten sam zabieg bezpieczeństwa dotyczy  komunikacji między różnymi domenami. Akurat  powiem, że kiedyś złapałem się na tym pisząc prostą aplikację pobierającą plik XML. Jeśli nasza aplikacja jest hostowana , uruchomiona pod adresem “http://cezarywalenciuk.blogspot.com” i próbuje użyć usługi sieciowej, która jest hostowana na “http://dropbox.pl” to zapytanie staje się między domenowe. Z powodu wymogów bezpieczeństwa aplikacja nie uruchomi usługi sieciowej.

Oczywiście problem polega na tym, że komu  ufać a komu nie. To da się rozwiązać. Silverlight tak jak kiedyś Flash wymagał od klienta aby aplikacja była hostowana na tej samej domenie. Gdy wykonuje się zapytanie między domenowe (sprawdzałem nawet w Fiddler2) Silverlight prosi automatycznie o plik xml “cross-domain-policy”, jeśli go nie znajdzie to serwer uznaje, że nie ma prawa użyć danego zasobu.

Plik clientaccesspolicy.xml  musi znajdować się w samym root.  domeny gdzie hostujemy naszą aplikację. Nawet jeśli prawidłowy plik istnieje, to jeśli się tam nie znajduje jest  bezużyteczny.

Jeśli plik xml ma poprawnie napisane  atrybuty to nasza aplikacja będzie miała dostęp do danego zasobu. Wzór pliku wygląda następująco i można sobie pomóc ściągając z Extension-Menagera w Visual Studio 2010 gotowy wzór  takiego pliku.

<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="/*Specify request headers like Content-Type,SOAPAction*/">
                <domain uri="/*Specify domains with granted access*/"/>
            </allow-from>
            <grant-to>
                <resource include-subpaths="false" path="/"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

Aby być bardziej zabawnym do pliku wystarczy dodać gwiazdkę “*” w “http-headers” i w “domain uri” i w sumie w ten sposób wszystkie zapytania zostaną uznane za poprawne. Na pewno w tym kursie będę wracał do tego jeszcze nie raz. Warto jednak się zapoznać z głębszą strukturą tego pliku.

Element/atrybutWymaganyOpis
access-policyTakElement root w pliku policy.
cross-domain-policyTakZawiera jedną bądź więcej elementów policy.
policyTakDefiniuje zasady dla pojedynczej domeny albo grup domen.
allow-fromTakInformacje o dozwolonych domenach, jeśli nie zawiera elementu to żaden dostęp nie jest zezwalany.
http-request-headersNieDefiniuje jaki nagłówek jest zezwalany, który został wysłany przez usługę sieciową w danej domenie. Jeśli jest pusty żadne nagłówki nie są dozwolone.
domainTakDefiniuje domenę.
uriTakDefiniuje jaki dokładnie może mieć dostęp.
grant-toTakJaki zasób ma być dostępny.
resourceTakWymagany dla klasy “WebClient” and “HttpWebRequest”. Definiuje jaki zasób powinien być użyty w danej polityce.
PathTakak jak wyżej. Format Uri jest relatywny względem początku domeny.
include-subpathsNieTak jak wyżej. Jeśli jest  pusty dostęp do podfolderów jest zabroniony.
socket-resourceTakWymagany przy dostępnie przez socket-y.Definiuje jaki socket jest dostępny.
PortTakWymagany przy dostępnie przez socket-y. Definuje porty w zasięgu od 4502 do 4534.
ProtocolTak<Wymagany przy dostępnie przez socket-y. Definiuje jakie protokoły są dostępne przez daną politykę. Jedynym protokołem, który jest teraz dostępny jest TCP.

 

Ograniczenie jeśli chodzi o Silverlight i plik Client acces policy:

  • Może być ona używana tylko przez klasy “WebClient” i “HttpWebRequest” oraz przez usługi korzystające z proxy . Sockety nie są dostępne.
  • Cała domena musi zezwolić na dostęp Silverlight do pliku crossdomain.xml. Silverlight nie obsługuje zawansowanych właściwości crossdomain.xml.
Po co to wszystko

Alternate Text Bezpieczeństwo jest zawsze najważniejsze. Akurat nie chciałbyś aby aplikacje takie jak Flash i Silverlight miały dostęp do wszystkiego albo żeby ktoś bez twojej zgody  korzystał z twoich plików, na twoim serwerze.

Prosta aplikacja symulująca zdarzenie cross-domain

Aby zrobić symulacje takiego zdarzenia stworzyłem dwa puste projekty web i dałem im określone porty. CrossDomainTest ma port 4000 a HostSilverlight ma port 9000. Stworzyłem prostą aplikację Silverlight, która zawiera jeden przycisk i jeden TextBlock. w “CrossDomainTest” dodałem plik xml “clienaccesspolicy.xml” oraz folder “zasob” a w nim plik txt, który pobierzemy i odtworzymy.

Aplikacja testująca zdarzenie CrossDomain

Układ Projektów i plik xml.

W Silverlight po naciśnięciu naszego przycisku zostanie pobrany plik za pomocą prostej klasy “WebClient”. Po asynchronicznym pobieraniu zajdzie zdarzenie “OpenReadComleted”. Sprawdzam czy plik pobrał się poprawnie po czym wyświetlam go w TextBlock-u TB_1.

private void button1_Click(object sender, RoutedEventArgs e)
{
  WebClient client = new WebClient();
  client.OpenReadCompleted += 
         new OpenReadCompletedEventHandler(client_OpenReadCompleted);
  string s = @"http://localhost:4000/Zasob/TextFile.txt"
  client.OpenReadAsync(new Uri(s, UriKind.Absolute));
}

void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
     if (!e.Cancelled && (e.Error == null))
     {
          using (TextReader writer = new StreamReader(e.Result))
          {
              TB_1.Text = writer.ReadToEnd();
          }
     }
     else
     {
          WebException webEx = e.Error as WebException;
          if (webEx != null)
          {
             TB_1.Text = webEx.Message;
          }
          else if (e.Error is SecurityException)
          {
             SecurityException secEX = e.Error
                   as SecurityException;
             TB_1.Text = secEX.InnerException.Message;
          }
     }
}

Pobierz Kod