User and Role Authorization

 

ASP.NET pozwala na skorzystanie z pewnych mechanizmów zarządzania użytkownikami oraz ich rolami. O możliwości ich konfiguracji z poziomu strony www pisałem w jednym z moich pierwszych postów na blogu. Tym razem chciałbym przedstawić w jaki sposób uzyskać pożądany efekt dokonując modyfikacji w kodzie.

Celem autoryzacji użytkowników jest przekazanie odpowiedniej treści, jedynie wybranym użytkownikom, czyli np. zablokowanie dostępu do pewnych elementów na stronie, czy też całych stron. Dodatkowo, użytkownikom mogą zostać przypisane role, dzięki czemu uzyskamy możliwość zarządzania pewną wydzieloną grupą użytkowników, przy czym każdy z nich może posiadać wiele ról.

Mechanizm, jaki chciałbym zaimplementować, jest dość standardowy. Użytkownik próbuje uzyskać dostęp do aplikacji, jednak bez względu na to, jaką stronę wpisał w adresie, jeśli nie jest już zalogowany, to jest przenoszony na stronę z kontrolką logowania. Po poprawnym podaniu danych logowania jest on przekierowywany na stronę, której ostatnio wyświetlenia żądał. Sprawdzenie, czy użytkownik może zostać zalogowany może następować np. poprzez sprawdzenie jego danych w bazie. W przykładzie jednak, dla mniejszej komplikacji, założone zostanie, że użytkownik podał poprawne dane. Ponadto informacja o tym, że ktoś został zalogowany powinna być dostępna na stałę – nie można na każdej ze stron wymagać logowania się przez osoby odwiedzające stronę. Wiemy już, że protokół http jest bezstanowy, zatem potrzebny jest jakiś mechanizm przechowywania informacji. W tym przypadku często wykorzystywane w tym celu są cookies, szczególnie w zaszyfrowanej wersji.

Przykład

Do “logowania” wykorzystam stworzoną stronę o nazwie MyLogin. Na stronie umieszczony został przycisk, który po kliknięciu będzie symulował sprawdzenie poprawności danych użytkownika i jego autoryzację. Przede wszystkim, należy dodać na początku kodu wpis:

using System.Web.Security;

Zdarzenie kliknięcia w przycisk obsługiwane jest w sposób następujący:

//Login button
protected void Button1_Click(object sender, EventArgs e)
{
	//Fill user data
	string userName = "Michal";
	string userRole = "Administrator";

	//Create ticket
	FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userName, DateTime.Now,
	DateTime.Now.AddMinutes(1), false, userRole, FormsAuthentication.FormsCookiePath);

	//Encrypt ticket
	string encryptedTicket = FormsAuthentication.Encrypt(ticket);

	//Create cookie
	HttpCookie aCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);

	//Send cookie to the browser
	Response.Cookies.Add(aCookie);

	//Redirect user to originated page
	string url = Request.QueryString["ReturnUrl"];
	Response.Redirect(url);
}

Powyższy kod wymaga nieco wyjaśnienia. Na początku, na stałę, zakodowane są poprawne dane użytkownika – jego nazwa oraz rola. Następnie tworzony jest obiekt zwany ticketem, który określa obiekt identyfikator użytkownika oraz dane dla niego (w tym przypadku będzie to nazwa roli). Tak utworzony ticket jest szyfrowany, a potem na jego podstawie tworzone jest cookie, które wysyłane jest do przeglądarki. Użytkownik żądając strony, do której nie posiada dostępu został przekierowany na stronę z logowaniem. Aby można było powrócić do strony, do której chciał się on dostać, jej adres przekazany został za pomocą query string. Pobierając ten adres możliwe jest przekierowanie na oryginalnie żądaną stronę. Chciałbym zwrócić w tym momencie uwagę na to, że pomimo iż dane o roli zostały zapisane, aby móc na ich podstawie dokonać autoryzacji niezbędne są dodatkowe czynności (autoryzacja użytkownika zadziała po niewielkich zmianach w pliku Web.config).

W pliku Web.config musi znaleźć się wpis:

<authentication mode="Forms">
	<forms loginUrl="MyLogin.aspx" defaultUrl="Default.aspx"></forms>
</authentication>

dzięki czemu niezalogowany użytkownik będzie przekierowywany na stronę MyLogin.aspx, a jeśli nie żądał on wcześniej innej strony to zostanie przekierowany na Default.aspx.

Dostęp do stron może być dla każdej z nich ustawiony w inny sposób. Powiedzmy jednak, że chcemy aby dostęp do jakiejkolwiek ze stron miał jedynie zalogowany użytkownik. W tym celu pomiędzy znacznikami <system.web></system.web> musi znaleźć się kod:

<authorization>
	<deny users="?"/>
</authorization>

Umieszczenie znaku zapytania w powyższej komendzie oznacza, że odrzuceni zostaną wszyscy użytkownicy, z wyjątkiem tych zalogowanych.

Chcielibyśmy jednak dodatkowo rozróżniać rolę użytkownika, czyli np. zezwólmy na dostęp do strony ManageUsers.aspx jedynie użytkownikowi z grupy “Administrator”. W pliku Web.config należy zatem dodać kolejny wpis:

<location path="ManageUsers.aspx">
	<system.web>
		<authorization>
			<deny users="?"/>
			<allow roles="Administrator"/>
			<deny users="*"/>
		</authorization>
	</system.web>
</location>

Aby jednak rola użytkownika była rozpoznawana przez aplikację, musimy udać się do pliku Global.asax i zaimplementować zdarzenie Application_AuthenticateRequest():

void Application_AuthenticateRequest(object sender, EventArgs e)
{
	if (HttpContext.Current.User != null)
	{
		if (HttpContext.Current.User.Identity.IsAuthenticated)
		{
			FormsIdentity id = HttpContext.Current.User.Identity as FormsIdentity;
			if (id != null)
			{
				//Get user ticket
				FormsAuthenticationTicket ticket = id.Ticket;

				//Get user's role
				string userRole = ticket.UserData;

				//using System.Security.Principal is needed
				HttpContext.Current.User = new GenericPrincipal(id, new string[] { userRole });
			}
		}
	}
}

W ten sposób, dostęp do strony ManageUsers.aspx mają jedynie zalogowani użytkownicy i to w dodatku przynależący do roli Administrator.

Reklamy

5 Responses to User and Role Authorization

  1. ksmetek says:

    Odkrylem małą dziwna rzecz, jednakze na szczescie w nieszczesciu nie jestem sam na swiecie z tym problemem. Otoz nie dziala expiration date dla ciastka. Wiekszosc ciastek konczy zywotnosc wraz z koncem sesji. Czytalem ze ponoc z jakims proxy to ma cos wspolnego. Na lokalnej maszynie, na IE w developers tools mozemy znalezc swoje ciastko i mimo ze mam ciastko ktore nie jest trwale, ktore ma ustawione w web.config 3 minuty i przy tworzeniu ticetu tez, narzedzie mi wyswietla ze wygasa wraz z koncem sesji. Ustawienie persistent na true lub false nic nie pomoglo. Dodam ze robie szyfrowanie ciastka metoda z asp.net, Nie mam niestety tyle czasu zeby kazdy wariatn sprawdzic. Jesli ktos moze niech sprawdzi czy bez protection end encrytpion wstawia prawidlowa date wygasniecia lub jesli ktos sie spotkal z tym problemem, to prosze o pomoc.

    • Moim zdaniem takie zachowanie jest jak najbardziej ok –

      „What you cannot read is the cookie’s expiration date and time. It turns out that when the browser sends cookie information to the server, the browser does not include the expiration information. You can read the Expires property, but it always returns a date-time value of zero.

      Earlier under Writing Cookies I mentioned that the browser is responsible for managing cookies; the Expires property is an example of this. The primary purpose of the Expires property is to help the browser perform housekeeping on its store of cookies. From the server’s perspective, the cookie either exists or it does not; the expiration is not a useful piece of information on the server side. Therefore, the browser does not provide this information when it sends the cookie. If you are concerned about the expiration date of a cookie, you must reset it, as I will explain shortly under Modifying and Deleting Cookies.

      To be clear, you can read the Expires property of a cookie that you have set in the Response object, before the cookie has been sent to the browser. However, you cannot get the expiration back in the Request object.”

      Źródło: http://msdn.microsoft.com/en-us/library/aa289495(VS.71).aspx

      • ksmetek says:

        Zrobilem reczne usuwanie ciastka, w przypadku, gdy nie zamykamy sesji. Dzieki rowniez za linka, teraz jestem spokojny ze to nie jest zadna anomalia, ale i tak nie rozumiem czemu inne ciastka maja pokazaną datę, skoro ja expiration date podgladam nie poprzez klasy .NET tylko z narzedzia w IE, ktore generuje z miejsca xmlowy plik. Rozumiem ze server albo widzi ciastko albo nie. Rozumiem tez z cytatu, ze przegladarka nie wysyla expiration date, tylko to ma jej pomgac w zarzadzaniu ciastkami, ok fajnie, ale tworzac bilet ja nadaje parametr expiration date. Z tego co na MSDN napisali, wnisokuje ze jest mowa o podgladzie jedynie za pomoca obiektów .net, a ja chcialbym zobaczyc expiration date u siebie na kompie w ciastku 😉

        No i czy to znaczy ze mam problem z ustawieniami indywitualnymi ogladjacego? Skoro ciastko mi ginie zaraz po zmaknieciu sesji, to jak ja bede trzymal te dane do nastepnego odwiedzenia mojej strony przez uzytkownika? Przeciez ciastka musza lezec, po to zeby moznabylo jakies ocje np skorek czy czegos innego trzymac w nich.

        W takim razie jak powinno sie napisac zarzadzanie biletem, zeby przegladarka nie usunela mi ciatka?

  2. Odnośnie przedostatniego akapitu Twojego ostatniego komentarza – w przypadku mechanizmu z użyciem FormsAuthenticationTicket mowa jest o ciasteczku autoryzacyjnym. Jeśli ginie Ci ciasteczko po zamknięciu sesji to jest to zachowanie jak najbardziej pożądane dla ciasteczka sesyjnego. Ze zwykłymi ciasteczkami nie powinno być takich kłopotów 🙂

    • ksmetek says:

      Jako ze nie znam innej opcji niz ustawienie atrybutu czy ma byc bilet periodyczny czy trwały, domniemam, że tylko ten atrybut ustala, czy ciastko jest esyjne, czy nie jest.

      W moim przypadku ustawienie wartosci na taką, aby ciastko bylo trwale, nie przynosilo rezultatu. Czytalem tez kiedys o tym później w internecie, niestey nie pamietam gdzie tobylo, bo nie potrzebowalem tej informacji na dlzuszą metę. Zapamietalem tylko, ze w IE jest jakis powod dla ktoregociastko nie zostaje na stałe. Niestety nie posiadam wiedzy na temat, jak zrobic by ciastko bylo stałe.

      Co wiecej, smiem twierdzić, że nie mam sesyjnego ciastka, poniewaz HTTP jest bezstanowy, a w Web.config ustawilem sobie . Wiec serwer odświeża ID sesji ktore obsluguje co 15 sek. Jezeli postawie sobie stronę na IIS, to zamykajac przegladarke nikt nie ma prawa wiedziec zaraz po zamknieciu, ze zamknalem sesje. Dopiero po tym interwale IIS sprawdza czy sesja jest nadal aktywna, jesli nie jest, to wykonuje Session_End. Bynajmniej taka posiadam wiedze z wlasnych obserwacji. Jesli sie myle to chetnie prosze o poprawienie mnie, bo jestem poczatkujacy w asp.net.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d blogerów lubi to: