ASP.NET Starter Kit Stefan Turalski Bezpieczne aplikacje internetowe na platformie ASP.NET 2.0 Wraz ze stworzeniem najnowszej wersji środowiska.net, firma Microsoft oddała w ręce programistów znacznie bardziej rozbudowaną platformę służącą do budowy aplikacji internetowych ASP.NET 2.0. Celem wprowadzenia rozwiązania.net w wersji 2.0 jest zapewnienie lepszego wsparcia pracy programistów posługujących się tą platformą. Jak zapewniają slogany reklamowe dzięki zastosowaniu najnowszego zestawu narzędzi i komponentów ASP.NET 2.0, czas, który potrzebujemy na stworzenie rozwiązań gotowych do funkcjonowania, będzie znacznie krótszy. Artykuł ten ma na celu zaprezentowanie tych z nowych możliwości ASP.NET 2.0, które wspierają twórców rozwiązań internetowych podczas projektowania, implementacji oraz zarządzania aspektami związanymi z zapewnieniem bezpieczeństwa aplikacji. Przedstawimy jak dzięki zastosowaniu nowych elementów ASP.NET 2.0, łatwiej i szybciej rozwiązać problemy związane z identyfikacją, autoryzacją i tworzeniem profili użytkowników. Autor pracuje na stanowisku - projektant oprogramowania w firmie Silicon & Software Systems Polska. Z zainteresowaniem przygląda się rozwojowi technologii IT, dawno już straciwszy nadzieję, że jest w stanie ogarnąć tą dziedzinę wiedzy. Kontakt z autorem stefan.turalski@gmail.com Rysunek 1. Koncepcja modelu dostawcy (j.ang. Provider Model) na przykładzie klas typu Membership Logistyka w IT czyli architektura dostaw Poznając platformę ASP.NET 2.0, spotkamy się koncepcjami takimi jak członkostwo, role i profile (j.ang. Profiles). To co łączy wymienione elementy to architektoniczny model dostawcy (j.ang. Provider Model). Opracowanie architektury pozwalającej na sprawniejsze tworzenie warstwy bezpieczeństwa wynika z dość powszechnej potrzeby opracowywania portali o bardzo podobnej funkcjonalności. Budując aplikację internetową musieliśmy lub z pewnością przyjdzie czas, gdy będziemy musieli zapewnić możliwość identyfikowania użytkowników, np. poprzez wprowadzenie funkcjonalności takich jak rejestrowanie użytkowników lub logowanie do naszego serwisu. Zestaw komponentów ułatwiających opracowanie wymienionych funkcjonalności istnieje niemal w każdej platformie wspierającej budowanie portali internetowych, w tym także w ASP.NET 1.1. Jednak najczęściej programista samodzielnie musi zaimplementować procedury odpowiedzialne za tworzenie konta użytkownika w bazie danych, odpowiednią weryfikację jego uprawnień podczas logowania, czy dostępu do stron portalu. Korzystając z ASP.NET 2.0 możemy wykorzystać, oprócz zestawu kontrolek, które opiszemy w dalszej części tego artykułu, już opracowany standardowy model zapewniający usystematyzowany sposób przechowywania danych dotyczących użytkowników naszego systemu oraz zestaw metod odpowiedzialnych za dostarczenie logiki biznesowej potrzebnej podczas realizowania zadań związanych z zabezpieczeniem systemu. Na Rysunku 1. przedstawiono jak wygląda koncepcja tego modelu. W ASP.NET 2.0 każda kontrolka może wykorzystać metody klasy Memership w celu wywołania metod związanych z warstwą bezpiecznego dostępu. Z poziomu klasy Membership używane są klasy typu MembershipProvider, które odpowiedzialne są za zapewnienie dostępu do warstwy persystencji naszego systemu. Na Rysunku 1. zaprezentowano w tej roli bazy danych Microsoft SQL Server w wersjach 2000 i 2005, Oracle 10g, Access, mysql, SQLLite, czy też usługi Active Directory. Wraz z wersją.net 2.0 firma Microsoft udostępniła dostawców dla bazy danych SQL Server wersjach 2000, Express i 2005 oraz Active Directory, także w postaci usługi ADAM. Istnienie innych implementacji MembershipProvider zawdzięczamy dobrze zdefiniowanej architekturze tego rozwiązania. Gdzie można odnaleźć istniejące implementacje, podpowie ramka Niestandardowe implementacje klasy typu MemebershipProvider. Jeśli, jak ma to miejsce najczęściej w przypadku nowych rozwiązań budowanych w ASP.NET 2.0, pracujemy wykorzystując bazę danych SQL Server, wówczas relacje zapisane w bazie danych zostaną przetworzone przez klasę SqlMembershipProvider. Jak już wcześniej wspomniano, można wykorzystać inne już istniejące implementacje dostawców. Można też stworzyć własną implementację, która rozszerza w specyficzny sposób już istniejącą funkcjonalność, co zaprezentowano w osobnej ramce po co to robić? 36 www.sdjournal.org Software Developer s Journal Extra! 20
W ten sposób możemy wykorzystać już istniejące kontrolki i inne niż wspierane źródło danych. Może się zdarzyć też tak, iż nie odpowiada nam mechanizm przechowywania danych zaimplementowany domyślnie w ASP.NET 2.0. Jednym z najczęstszych powodów opracowywania własnej implementacji dostawców okazuje się być istniejąca już struktura bazy danych zapewniająca przechowywanie danych definiujących użytkowników, której nie chcemy zmieniać lub rozbudowywać. Wszak dużym ryzykiem jest poleganie na schemacie bazy danych automatycznie tworzonej przez Visual Studio na potrzeby dostawców. Szczególnie, gdy dzięki opracowaniu własnego dostawcy, możemy dodatkowo uniknąć migracji w warstwie danych. Zaprezentowana na Rysunku 1. klasa typu Membership, w przypadku zastosowania innych typów dostawców, może być zastąpiona przez klasy typu Profile lub Roles. W ten sposób bardzo podobny stos wywołania metod można wykorzystać nie tylko podczas budowania strony rejestracji nowych użytkowników, ale także w celu sprawdzenia, do jakiej roli należy dany użytkownik. Klasy zapewniające usługi związane z bezpieczeństwem w ASP.NET 2.0 to pięć głównych typów dostawców. Omówiony pokrótce Membership Provider odpowiedzialny za zapewnienie identyfikacji użytkowników, Role Provider umożliwiający autoryzację użytkownika i powiązanie go z odpowiednią rolą jaką użytkownik odgrywa w systemie. Następnie mamy do czynienia z klasą Profile Provider, zapewniającą wsparcie dla personalizacji stron portalu, SiteMap Provider, czyli klasą odpowiedzialną za interakcje z mapą witryny oraz Session State Store Provider udostępniającą dostęp do zasobów związanych z sesją użytkownika. Pokrótce postaramy się zaprezentować jak można efektywnie wykorzystać te elementy przy budowaniu własnej, bezpiecznej, aplikacji internetowej. Listing 1. Przykładowa zawartość Web.config <?xml version="1.0" encoding="utf-8"?> <connectionstrings> <remove name="localsqlserver"/> <add name="localsqlserver" connectionstring="data Source=localhost;Initial Catalo g=aspnetdb;integrated Security=True" providername="system.data.sqlclient"/> </connectionstrings> Szczegóły pracy dostawcy Implementacja każdej klasy typu Provider w ASP.NET 2.0 zakłada dziedziczenie z abstrakcyjnej klasy ProviderBase obecnej w przestrzeni nazw System.Configuration. Klasa ta definiuje metodę Initialize(), która, jak można się spodziewać, jest odpowiedzialna za wczytanie danych konfiguracyjnych z pliku web.config. Wspomniana klasa MembershipProvider dziedzicząca z ProviderBase, znajduje się w przestrzeni nazw System.Web.Security. Główna funkcjonalność tej klasy to zapewnienie odpowiedniej funkcjonalności dla identyfikacji użytkowników, w tym wspomnianego tworzenia użytkowników lub zarządzania już istniejącymi kontami użytkowników. Klasa MemebershipProvider jest także klasą abstrakcyjną, przez co twórcy architektury niewątpliwie chcieli podkreślić, iż istniejąca implementacja to jedynie szablon dla dopiero powstających konkretnych zastosowań modelu dostawcy. W przypadku dostępu do bazy danych będziemy potrzebowali skorzystać z jednej z już opracowanych specjalizowanych wersji klasy typu MembershipProvider. Mowa tu o SqlMembership - klasie znajdującej się tej samej przestrzeni nazw (System.Web.Security). Diagram klas pozwalających na realizację identyfikacji, autoryzacji i przypisania użytkownika do odpowiedniej roli zawiera Rysunek 2. Sprawna konfiguracja dostaw Aby wykorzystać funkcjonalność oferowaną przez klasę Sql- MembershipProvider, musimy odpowiednio skonfigurować środowisko dostawców. Przede wszystkim, aby dane były przechowywane w bazie danych, musimy stworzyć odpowiednie wpisy w pliku web.config oraz odpowiednie tabele. Może to zostać wykonane na dwa sposoby. Pierwszy z nich to wykorzystanie narzędzia ASP.NET Website Administration Tool, za którego pomocą możemy stworzyć odpowiedni schemat bazy danych SQL Server 2005 w pliku ASPNETDB.mdf, w specjalnym folderze naszego projektu App_Data. Możemy też posłużyć się bardziej standardową metodą i korzystając z narzędzia ASP.NET SQL Server Registration Tool stworzyć odpowiednie tabele w bazie danych SQL Server 2000 lub 2005. Aby stworzyć bazę danych w pliku ASPNETDB.mdf, bazy danych SQL Express, należy uruchomić ASP.NET Website Administration Tool. Narzędzie to dostępne jest z menu Visual Studio, wystarczy wybrać Website->ASP.NET Configuration i z poziomu webowego interfejsu możemy rozpocząć proces konfiguracji naszej aplikacji. Na zakładce odpo- Rysunek 2. Schematy dziedziczenia w modelu dostawcy dla serwera SQL (j.ang. Provider Model) Rysunek 3. Aplikacja ASP.NET Website Administration Tool Software Developer s Journal Extra! 20 www.sdjournal.org 37
ASP.NET Starter Kit wiedzialnej za konfigurowanie zabezpieczeń (j.ang. Security) ustawimy typ identyfikacji użytkownika na pochodzącego z Internetu (j.ang. From the Internet). Możemy to zrobić przechodząc przez kroki konfiguracji zabezpieczenia naszej witryny wybierając link Use the security Setup Wizard to configure security step by step, gdzie w drugim kroku należy podać odpowiednią opcję lub prościej wybieramy link Select authentication type i zapisujemy nowe ustawienia a następnie możemy tworzyć nowych użytkowników. Ilustracją służy Rysunek 3. W obu przypadkach stworzona zostanie baza danych, w której będą znajdować się dane dotyczące użytkowników naszej aplikacji. W schemacie tej bazy danych na potrzeby klasy Sql- MemebershipProvider zapisane są informacje o użytkowniku w dwóch powiązanych tabelach. W tabeli aspnet_users znajdują się podstawowe dane o każdym użytkowniku, w tym kolumna UserID, w której przechowywane są wartości GUID. Wartość ta służy do powiązania danych z tabeli aspnet_users z danymi użytkownika przechowywanymi w tabeli aspnet_membership. Dodatkowo w wyniku działania narzędzia konfigurującego w pliku Web.config powstanie wpis: <authentication mode="forms" /> oraz odpowiednio skonfigurowana ścieżka wykazująca na bazę danych ASPNETDB.mdf. W związku z faktem, że w zastosowaniach biznesowych pracujemy częściej z pełną wersją serwera, zaprezentujemy też kilka kroków prowadzących do stworzenia struktury potrzebnej dla klasy i funkcjonalności SqlMembershipProvider w standardowym środowisku SQL Server 2000. Listing 2. Specyficzna konfiguracja w pliku Web.config <?xml version="1.0" encoding="utf-8"?> <connectionstrings> <add name="myconnectionstring" connectionstring="data Source=localhost;Initial Catalog=aspnetdb;Integrated Security=True" providername="system.data.sqlclient"/> </connectionstrings> <system.web> <authentication mode="forms" /> <membership defaultprovider="myprovider"> <providers> <remove name="aspnetsqlprovider" /> <add name="myprovider" connectionstringname="myconnectionstring" minrequiredpasswordlength="4" minrequirednonalphanumericcharacters="0" enablepasswordretrieval="false" enablepasswordreset="false" requiresquestionandanswer="false" type="system.web.security.sqlmembershipprovider" </providers> </membership> </system.web> /> Rysunek 4. Kontrolka Login w trybie design i w działaniu Z linii poleceń uruchamiamy narzędzie aspnet_regsql.exe znajdziemy je w folderze C:\Windows\Microsoft.NET\Framework\ v2.0.*. Kreator konfiguracji ASP.NET SQL Server krok po kroku przeprowadzi nas przez zadania, takie jak: tworzenie odpowiednich tabel, procedur składowanych itd. Pierwszy ekran kreatora to krótkie opisanie opcji, na drugim ekranie możemy wybrać czy będziemy tworzyli nową bazę danych czy też usuwali dane z już istniejącej bazy. Oczywiście chodzi nam o opcję Configure SQL Server for application services, klikamy więc [Next]. Przechodzimy do ekranu, na którym możemy wybrać serwer, typ uwierzytelniania i bazę danych, w której będą przechowywane dane. Potwierdzamy ustawienia klikając [Next] i kończymy pracę z kreatorem. Tabele i procedury składowane są już przygotowane, teraz należy odpowiednio skonfigurować środowisko ASP.NET. Można tego dokonać na wiele różnych sposobów, jednym z nich jest uruchomienie konsoli zarządzania i konfiguracji serwera IIS. Na zakładce ASP.NET w oknie właściwości naszej aplikacji wybieramy przycisk [Edytuj konfigurację] i w polu LocalSqlServer edytujemy dane połączenia z serwerem, na którym właśnie założyliśmy bazę danych, np. Data Source=localhost;Initial Catalog= aspnetdb;integrated Security=True. Jeśli nie używasz serwera IIS do tworzenia aplikacji, odpowiednie wpisy powinny znaleźć się w pliku konfiguracyjnym aplikacji. Mając skonfigurowaną bazę danych możemy pokusić się także o odpowiednie skonfigurowanie właściwości naszego dostawcy. Na Listingu 2. przedstawiono, jak można zmienić opcje takie jak domyślna długość hasła minrequiredpasswordlength lub liczba znaków innych niż alfanumeryczne minrequirednonalphanumericcharacters. Zostały także wyłączone możliwości odzyskiwania i ponownego ustawienia hasła, jak i prośba o podanie pytania i odpowiedzi potrzebnych do uwierzytelnienia użytkownika. Jeżeli chcemy użyć tutaj własnej nazwy połączenia, możemy uniknąć nadpisywania domyślnie skonfigurowanego połączenia o nazwie LocalSqlServer, poprzez wskazanie odpowiedniej wartości connectionstringname. Tak skonfigurowany dostawca, mimo iż wykorzystuje ten sam schemat bazy danych i klasę odpowiedzialną za interakcje z kontrolkami interfejsu użytkownika, podczas inicjalizacji zostanie odpowiednio zanicjalizowany Więcej o opcjach konfiguracyjnych możemy przeczytać w sieci (patrz ramka Biografia i przydatne linki w Sieci ASP.NET Configuration Setting). Tak jak i w przypadku konfiguracji wykonanej od początku z poziomu ASP.NET Website Administration Tool także teraz możemy swobodnie dodawać użytkowników naszego systemu. 38 www.sdjournal.org Software Developer s Journal Extra! 20
Rysunek 5. Konfiguracja reguł dostępu do zasobów Wykorzystanie dostaw Odpowiednie skonfigurowany system, w którym używamy modelu dostawcy, wykorzystujemy głównie dzięki kontrolkom webowym oferowanym w środowisku ASP.NET 2.0. Naturalnym więc etapem, po odpowiednim dostrojeniu systemu, będzie zbudowanie funkcjonalności, dzięki której baza danych i stworzone w niej tabele będą mogły być wykorzystane z poziomu stron naszego portalu. Już w poprzedniej wersji środowiska (ASP.NET 1.1) mieliśmy do czynienia z bogatą kolekcją komponentów graficznych przyspieszających pracę programisty. W najnowszej wersji środowiska funkcjonalność w zakresie zapewnienia bezpiecznego dostępu oferują kontrolki: CreateUserWizard, Login, LoginName, LoginStatus, LoginView, PasswordRecovery i ChangePassword. Jak nietrudno się domyślić, nie pisząc nawet linijki kodu kontrolkę wystarczy przeciągnąć na nowo utworzoną stronę ASP, aby wykorzystać oferowaną funkcjonalność. Jeśli stworzyliśmy zestaw styli CSS dla naszej aplikacji webowej, bez problemu możemy wspomniane kontrolki dostosować, możemy też wykorzystać szablony, o których mowa w artykule Strony wzorcowe i motywy graficzne w ASP.NET 2.0. Kontrolka Login zapewnia funkcjonalność polegającą na podaniu nazwy i hasła użytkownika. W momencie kliknięcia przycisku [Log In] lub po prostu [Zaloguj] po zainstalowaniu polskiego pakietu językowego - ze stosu klas typu Membership zostanie wywołana metoda VerifyUser, do której jako parametry zostaną wysłane wartości username i password. Jeżeli dane podane przez użytkownika są poprawne, automatycznie otrzyma on dostęp do serwisu i zostanie stworzony dla niego pakiet danych uwierzytelniający użytkownika. W przeciwnym przypadku kontrolka wyświetli odpowiedni komunikat błędu. Oczywiście takie domyślne zachowanie możemy stosunkowo łatwo rozszerzyć lub zmodyfikować. Na Rysunku 4. widzimy okno Visual Studio, w którym zaprezentowano listę zdarzeń wyzwalanych przez kontrolkę Login. Jeżeli chcemy zapewnić inną od standardowej obsługę procesu identyfikacji użytkownika, należy obsłużyć zdarzenie Authenticate. W przypadku gdy np. chcemy wyświetlić odpowiedni komunikat po podaniu błędnych danych, możemy odpowiednio zaimplementować obsługę zdarzenia LoginError. Dzięki kontrolce Login oraz funkcjonalności klasy SqlMembershipProvider, dysponujemy już możliwością identyfikacji użytkownika. Aby skorzystać z tej funkcjonalności możemy wykorzystać kontrolkę LoginView w celu odpowiedniego filtrowania treści w zależności od stanu użytkownika. W przypadku, gdy do naszego systemy nie zalogował się jeszcze żaden użytkownik, kontrolka ta wyświetlać będzie domyślą zawartość. Dysponując standardowymi szablonami AnonymousTemplate i LoggedInTemplate odpowiednio dla niezidentyfikowanego oraz już znanego użytkownika możemy zapewnić kontrolę nad informacjami przekazywanymi każdemu z nich. Co istotne w momencie, gdy aplikacja nasza będzie wykorzystywać nie tylko dostawcę typu Membership ale także i wspierającego tworzenie ról użytkowników, kontrolkę LoginView bardzo łatwo skonfigurować tak, aby filtrowała treść także w zależności od roli, w której jest użytkownik. W celu wyświetlenia nazwy użytkownika możemy posłużyć się znanym z ASP.NET 1.1. fragmentem kodu User.Identity.Name, opracowano także kontrolkę LoginName, która wykonuje identyczne zadanie. Kolejnym ukłonem w stronę tych, którzy cenią sobie oszczędność czasu, jest kontrolka LoginStatus zapewniająca, iż użytkownikowi niezalogowanemu zostanie pokazany link do strony z oknem logowania, natomiast użytkownik zalogowany będzie miał opcję wylogowania się z serwisu. Funkcjonalności kontrolek PasswordRecovery i ChangePassword nie trzeba chyba tłumaczyć. Każdy komu zdarzyło się zapomnieć hasła w serwisie, z pewnością wie, jak wygląda proces odzyskiwania hasła. W ASP.NET 2.0 możemy skonfigurować system tak, aby wysyłał hasło na adres konta e-mail użytkownika. Możemy także udostępnić opcję zmiany hasła poprzez proste przeciągnięcie kontrolki w żądane miejsce strony internetowej. Spośród wymienionych kontrolek, programistom najbardziej przypada do gustu kontrolka służąca do tworzenia nowego użytkownika. Komponent CreateUserWizard pozwala na swobodne wprowadzanie danych przez użytkownika, zapewnia też walidację wprowadzonych danych. W przypadku, gdy potrzebujemy rozszerzyć zestaw danych opisujących użytkownika w naszej aplikacji, pisząc własnego dostawcę stosunkowo łatwo możemy poszerzyć listę danych, które zostaną wprowadzone za pomocą tejże kontrolki. Jeśli okazuje się, że dostępne kontrolki to jednak wciąż za mało, z pomocą przychodzi tzw. Membership API. Zestaw Listing 3. Specyficzna konfiguracja w pliku Web.config na potrzeby uruchomienia ról <?xml version="1.0"?> <connectionstrings>[..]</connectionstrings> <system.web> <authentication mode="forms"/> <membership defaultprovider="myprovider">[..] </membership> <rolemanager enabled="true" defaultprovider="myrolepro <providers> vider"> <add name="myroleprovider" </providers> </rolemanager> </system.web> type="system.web.security.sqlroleprovider" connectionstringname="myconnectionstring" /> Software Developer s Journal Extra! 20 www.sdjournal.org 39
ASP.NET Starter Kit metod oferowanych przez klasy Membership pozwala na pobranie wszystkich użytkowników systemu, odszukanie osób o danym adresie e-mail, etc dzięki już istniejącym procedurom składowanym i zaimplementowanej logice, zadania tego typu sprowadzają się praktycznie do opracowania odpowiedniego komponentu graficznego. Rola ról Niemniej ważną cechą pozwalającą na tworzenie bezpiecznych aplikacji webowych w środowisku ASP.NET 2.0 jest dostawca ról. Stosunkowo łatwo można wyobrazić sobie sytuację, w której sama tylko identyfikacja użytkownika nie wystarczy do zapewnienia pełnej kontroli nad odpowiednim zarządzaniem dostępem do treści opublikowanych na stronach portalu. Szablonowym przykładem może być portal organizacji podzielonej na departamenty. Choć wszyscy pracownicy mogą czytać dane na niemal każdej stronie, to za edycję odpowiedzialne są najczęściej wybrane osoby. Jeśli do przykładowego scenariusza dodamy np. funkcjonalność publikacji raportów o wynikach firmy, dostępnych tylko dla kadry zarządzającej i hobbistycznie prowadzony podportal rozgrywek brydżowych, o którym wspomniana kadra niekoniecznie musi wiedzieć, okazuje się że mamy do czynienia z całkiem złożonym tworem, dość skomplikowanym w zarządzaniu. W ASP.NET 2.0 w znanych już nam strukturach bazy danych SQL lub usługi Active Directory możemy stworzyć i wykorzystać role, które pozwolą nam na grupowanie użytkowników. Podobnie jak w przypadku dostawców typu Membership mamy do czynienia z dostępnymi dostawcami usług typu Roles. W omówionym poprzednio przykładzie skupiliśmy się na klasie SqlMembershipProvider, w przypadku ról przez analogię będzie to klasa SqlRoleProvider. Klasa ta operuje na tabelach uprzednio stworzonej bazy SQL, użytkownicy już uprzednio założeni i dodani do tabeli aspnet _ Users, są odpowiednio przypisywani do ról przechowywanych w tabelach aspnet _ Roles i aspnet _ UsersIn- Roles. W przypadku zastosowania dostawcy korzystającego z usługi Active Directory wykorzystamy klasę AuthorizationStoreRoleProvider lub WindowsTokenRoleProvider, gdy chcemy pobrać definicje ról z lokalnych definicji systemu Windows. Biografia i przydatne linki w Sieci Professional ASP.NET 2.0 Security, Membership, and Role Management, Stefan Schackow, Wrox, 2006.01.31 ASP.NET 2.0 Provider Model: Introduction to the Provider Model http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnaspp/html/aspnetprovmod_intro.asp Provider Toolkit: http://msdn.microsoft.com/asp.net/downloads/providers/ ASP.NET Configuration Setting: http://msdn2.microsoft.com/en-us/library/b5ysx397.aspx Security Guidelines: ASP.NET 2.0: http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag2/html/pagguidelines0001.asp patterns & practices Security How Tos Index (w tym także odpowiedzi na pytania dot.asp.net 2.0): http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag2/html/securityhowtosindex.asp Listing 4. Ograniczenie uprawnień do katalogu w pliku web.config <?xml version="1.0" encoding="utf-8"?> <system.web> <authorization> <allow roles="kucharze" /> <deny users="barman_01" /> </authorization> </system.web> Analogicznie jak w poprzednim przypadku możemy włączyć wykorzystywanie ról z poziomu ASP.NET Website Administration Tool lub bezpośrednio modyfikując plik konfiguracyjny Web.config. Taka konfiguracja pozwoli nam na dodawanie nowych ról z poziomu konsoli administracyjnej, a następnie przypisywanie użytkowników do dodanych właśnie ról. Wybierając z zakładki zabezpieczenia (j.ang. security) opcję Utwórz role lub zarządzaj nimi (j.ang. Create or Manage roles) możemy dodać nową rolę, usunąć rolę już dodaną lub wybierając na nowej stronie opcję Zarządzaj przypisać użytkowników do tej roli. Jako, że role same w sobie są jedynie wpisami w bazie danych, musimy jeszcze zapewnić ich respektowanie w warstwie logiki i interfejsu naszej aplikacji internetowej. W poprzedniej wersji środowiska role użytkownika z systemu Windows należało odpowiednio konfigurować przy użyciu wpisów w plikach config. I jak pamiętamy na każdym poziomie zagnieżdżenia strony, jeśli chcieliśmy zablokować dostęp, należało stworzyć sekcje wykluczającą dany typ użytkownika. Koncepcja ta została przeniesiona do ASP.NET 2.0 z tym, że teraz to z poziomu konsoli administracyjnej możemy budować te dość skomplikowane reguły. Po skończonej konfiguracji plik Web.config w dowolnym podkatalogu naszej aplikacji będzie służył jako źródło informacji dla platformy ASP.NET 2.0, czy użytkownik posiada czy też nie uprawnienia wymagane do uzyskania dostępu do danego zasobu. Oczywiście wciąż możemy ingerować w pliki konfiguracyjne ręcznie, co zostanie odpowiednio odzwierciedlone podczas korzystania z konsoli administracyjnej ASP.NET 2.0. Na podobnej zasadzie jak podczas ograniczania dostępu do zasobów, możemy sterować treścią wyświetlaną przez kontrolkę LoginView. Jak pamiętamy, posiada ona dwa szablony, jeden dla użytkowników nieautoryzowanych w naszym systemie i drugi dla użytkowników poprawnie zalogowanych. W celu wykorzystania ról, modyfikujemy odpowiednio ustawienia kontrolk. Tak jak w przypadku konfiguracji dostępu do katalogów, lista zasad jest przetwarzana w odpowiedniej kolejności. Użytkownik w roli kucharze będzie widział treść odpowiadającą jego sekcji, w przypadku gdy użytkownik należy do obu grup (jest zarówno barmanem jak i kucharzem), wówczas zobaczy zawartość pierwszej w kolejności sekcji. Można również skonfigurować treść wyświetlaną w powyższej kontrolce tak, aby dla kilku grup prezentowała identyczny tekst, w tym celu wpisujemy nazwy grup po przecinku jako wartości atrybutu Roles. 40 www.sdjournal.org Software Developer s Journal Extra! 20
Podobnie jak w przypadku Membership API, istnieje zestaw metod, dzięki którym możemy opracować własne kontrolki zapewniające dedykowaną funkcjonalność opartą o funkcjonalność klas RolesProvider. Konsola administracyjna ASP.NET do działania wykorzystuje metody takie jak CreateRole(), DeleteRole(), GetRolesForUser() itp. Z pewnością nie przysporzy więc problemu ewentualne rozbudowanie już istniejących kontrolek tak, aby np. umożliwić edytowanie przynależności użytkowników do ról z poziomu portalu. Profile użytkownika Ostatnim z aspektów budowania bezpiecznej aplikacji webowej, który zostanie poruszony w tym artykule dotyczy budowania profili z wykorzystaniem klasy SqlProfileProvider (implementacji abstrakcyjnej klasy ProfileProvider). Aby wykorzystać profile musimy najpierw zdefiniować, jakie wartości będą nas interesować. Jak zapewne uważny czytelnik już się domyśla, możemy konfigurację taką stworzyć bezpośrednio w pliku Web.config. Co ważne wystarczy tylko definicja tych danych, aby w tabeli aspnet _ Profiles powstał odpowiedni wpis, gdy tylko użytkownik zapisze swoje domyślne wartości po raz pierwszy. Odwołanie się do wartości wymaga posłużenia się składnią Profile.<nazwa atrybutu>. Konfiguracja atrybutów, które podlegają profilowaniu, ma miejsce już w powyższym przypadku. Aby umożliwić dostęp do wartości profilowanych niezalogowanym użytkownikom trzeba na to zezwolić stosując atrybut allowanonymous zainicjowany z wartością true. Aby umożliwić dostęp i rozróżnianie użytkowników anonimowych w pliku Web.config musimy dodać linię: <anonymousidentification enabled="true"/>. Spowoduje ona, że dla każdego anonimowego użytkownika zostanie stworzone ciasteczko zawierając GUID stanowiący nazwę użytkownika. Tak długo, jak użytkownik nie usunie pliku go identyfikującego, będzie mógł korzystać z zapisanych poprzednio wartości. Możemy też zainicjalizować wartość podlegającą profilowaniu domyślną wartością podaną jako atrybut defaultvalue. Wartości zapisane w profilu mogą być też grupowane, wówczas dostęp do nich wymaga bezpośredniego odwołania się do nazwy grupy Profile.<nazwa grupy>.<nazwa atrybutu> Co ciekawe na podstawie definicji profili w czasie działania aplikacji tworzona jest klasa ProfileCommon, która odzwierciedla strukturę i typy zdefiniowanych atrybutów. Dzięki takiemu podejściu rozwiązania oparte o profile łatwiej debugować, a w razie potrzeby możemy odwołać się bezpośrednio do kodu tejże klasy: ProfileCommon profile = HttpContext.Current.Profile as ProfileCommon; Warto nadmienić, że w profilu możemy przechowywać wartości dowolnego typu, który zdefiniujemy w aplikacji. Tak więc tworząc klasę np. Kredyt, dodając linię <add name="kredyt" type="kredyt" serializeas="binary"/> w pliku Web.config, zapewniamy iż wartość ta będzie odpowiednio przechowywana w bazie danych. Przy czym domyślnie wartości danej klasy przechowywane są w postaci wpisów XML, dopiero po ustawieniu opcji serializeas na serializację binarną otrzymamy taki też zapis danych. Jak widzimy profilowanie to temat znacznie wykraczający poza podstawowe aspekty związane z bezpieczeństwem aplikacji, aczkolwiek niewątpliwie pomaga stworzyć w pełni spersonalizowany i bezpieczny portal. Niestandardowe implementacje klasy typu MemebershipProvider W momencie tworzenia tego artykułu, a więc w kilka miesięcy po opublikowaniu ostatecznej wersji środowiska ASP.NET 2.0, oprócz dostarczanych wraz z platformą dostawców usług dla bazy danych Microsoft SQL Server (w wersjach 2000, Express i 2005) oraz Acive Directory, dostępny jest także szereg, na razie raczej hobbistycznych projektów umożliwiających pracę z innymi systemami. Do najbardziej znanych, dostępnych wraz z kodem źródłowym należą: Dostawca dla bazy danych Access, autorstwa firmy Microsoft: Access Database Support for ASP.NET Membership, Roles and Personalization http://msdn.microsoft.com/vstudio/eula.aspx?id=96713a8eb8d4-4d6e-bb8f-027e6c8e15d8 Dostawca usług Membership i Roles dla serwera SQLite w wersji 3.0 autorstwa Peter A. Bromberg a: SQLite 3.0 Membership and Role Provider for ASP.NET 2.0 http://www.eggheadcafe.com/articles/20051119.asp Dostawca usług Membership oraz Roles dla bazy danych my- SQL, autorstwa Rakotomalala Andriniaina: Membership and Role providers for MySQL http://www.codeproject.com/aspnet/mysqlmembershipprovider.asp Dostawca usług Membership, Roles i Presonalization, autorstwa firmy Microsoft a wchodzący w skład najnowszej, już czwartej, wersji przykładowej aplikacji PetShop 4.0 : Microsoft.NET Pet Shop 4: Migrating an ASP.NET 1.1 Application to 2.0 http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnbda/html/bdasamppet4.asp Wciąż powstają także warstwy dostawców specjalizowane dla konkretnych zastosowań, takich jak np. OpenLDAP lub własnych struktur danych już istniejących w wielu organizacjach. Podsumowanie Omówiliśmy główne elementy związane z wdrażaniem bezpiecznych aplikacji w najnowszej wersji platformy ASP.NET. Poruszyliśmy więc temat modelu dostawców. Przestawiliśmy jak pracują, jak skonfigurować i wykorzystać elementy, takie jak dostawcy typu: Membership, Roles i Profile. Dość pobieżnie wprowadziliśmy przegląd kilku kontrolek wspomagających prace z aspektami dotyczącymi zabezpieczenia aplikacji. Przede wszystkim jednak nakreśliliśmy obraz tego, co zostało stworzone w platformie ASP.NET 2.0 i na co programista powinien zwrócić szczególną uwagę. Czy elementy te faktycznie przyspieszą jego pracę i ułatwią tworzenie bezpiecznych aplikacji, czas zapewne pokaże. Jednak już dziś można powiedzieć, iż w porównaniu z poprzednią wersją (ASP.NET.1.1), obecnie oferowana funkcjonalność jest znacznie bardziej przejrzysta i przyjazna użytkownikowi. Stanowi też bardzo korzystną ofertę na rynku platform programistycznych budowanych z myślą o następnych generacjach rozwiązań internetowych. ASP.NET 2.0 to z pewnością produkt wobec którego nie wypada przejść obojętnie, także ze względu na model bezpieczeństwa w nim zaprezentowany. Któż z nas nie chciałby skupić się na biznesowych aspektach aplikacji, ufając iż warstwa bezpieczeństwa jest gwarantowana przez zastosowaną technologię? Miejmy nadzieję, iż ASP.NET 2.0 wytrzyma próbę rynkową i prócz niewątpliwego przyspieszenia procesu tworzenia rozwiązań webowych, zapewni także ich bezpieczeństwo. Software Developer s Journal Extra! 20 www.sdjournal.org 41