70-536: Authenticating and Authorizing Users

Poniższy artykuł pochodzi z serii Przygotowań do egzaminu 70-536.

Framework .NET pozwala korzystać z systemowego systemu zabezpieczeń opartego na rolach (ang. RBS, Role-based security), Active Directory, lub własnych mechanizmów. Dzięki temu możemy kontrolować do jakich zasobów, czy funkcji użytkownik ma dostęp. Programowo możemy zarządzać autoryzacją (ang. authorization) i uwierzytelnianiem użytkowników (ang. authentication):

  • Uwierzytelnianie – weryfikuje tożsamość użytkownika, określa to kim jest użytkownik; zwykle zachodzi na podstawie nazwy użytkownika i hasła, mogą do tego służyć metody biometryczne (siatkówka oka), czy karty magnetyczne,
  • Autoryzacja – stwierdza, czy użytkownik ma uprawnienia, aby odwołać się do konkretnych zasobów, odpowiedzialna jest za określanie poziomu uprawnień.

Uwierzytelnianie zachodzi zawsze przed autoryzacją.

Zdobywanie informacji o użytkowniku

Dane użytkownika

Klasa WindowsIdentity reprezentuje konto użytkownika, daje dostęp do nazwy użytkownika, typu uwierzytelniania, czy tokenu uwierzytelniania. Aby z niej korzystać należy dodać właściwą dyrektywę:

   1: using System.Security.Principal;

Przykład kodu korzystającego z klasy:

   1: WindowsIdentity identity = WindowsIdentity.GetCurrent();
   2:  
   3: Console.WriteLine("Name: " + identity.Name);
   4: Console.WriteLine("Token: " + identity.Token.ToString());
   5: Console.WriteLine("Authentication Type: " + identity.AuthenticationType);
   6:  
   7: if (identity.IsAnonymous)
   8:     Console.WriteLine("Is an anonymous user");
   9: if (identity.IsAuthenticated)
  10:     Console.WriteLine("Is an authenticated user");
  11: if (identity.IsGuest)
  12:     Console.WriteLine("Is a guest");
  13: if (identity.IsSystem)
  14:     Console.WriteLine("Is part of the system");

Metoda GetCurrent zwraca obiekt WindowsIdentity dla aktualnego użytkownika. Jeśli chcielibyśmy otrzymać obiekt dla anonimowego, nieautoryzowanego użytkownika, należałoby wywołać metodę GetAnonymous.

W celu sprawdzenia kodu przykład po skompilowaniu najlepiej raz uruchomić jako zwykły użytkownik, raz jako administrator.

Grupy użytkownika

Obiekt WindowsPrincipal i jego metoda IsInRole pozwalają określić nam do jakich grup należy użytkownik. Dla grup wbudowanych możemy skorzystać z typu wyliczeniowego WindowsBuiltInRole i jego zdefiniowanych pól:

imageRozszerzmy nasz poprzedni przykład o sprawdzanie trzech przykładowych grup. Najpierw odpowiednia dyrektywa using:

   1: using System.Security.Permissions;

Oraz właściwy kod:

   1: if (identity.IsAnonymous)
   2:     Console.WriteLine("Is an anonymous user");
   3: if (identity.IsAuthenticated)
   4:     Console.WriteLine("Is an authenticated user");
   5: if (identity.IsGuest)
   6:     Console.WriteLine("Is a guest");
   7: if (identity.IsSystem)
   8:     Console.WriteLine("Is part of the system");

Sprawdzić niestandardowe grupy możemy podając metodzie IsInRole string:

   1: if (principal.IsInRole(@"kml-Komputer\kml")){
   2:     Console.WriteLine("Hello kml!");
   3: }

Lub korzystając ze zmiennych systemowych:

   1: if (principal.IsInRole(System.Environment.MachineName + @"\kml")){
   2:     Console.WriteLine("Hello kml!");
   3: }

Jak można zauważyć string przybiera formę “DOMENA\Nazwa grupy”.

Wymuszanie kontroli uprawnień

Wymuszenia, aby użytkownik był uwierzytelniony, lub należał do konkretnej grupy możemy wykonać w sposób deklaratywny i imperatywny. Obie metody wymagają załadowania System.Security.Principal:

   1: using System.Security.Principal;

oraz zmianę domyślnej polityki:

   1: System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

Kontrola imperatywna

Tworząc obiekt klasy System.Security.Permissions.PrincipalPermission możemy określić, czy użytkownik musi być uwierzytelniony, sprawdzić konkretną nazwę użytkownika, czy rolę. Nic więcej! Możemy pominąć któreś z wymagań wstawiając null.

   1: string r = @"BUILTIN\Administratorzy";
   2: // Catch any security denied exceptions so that they can be logged
   3: try
   4: {
   5:     // Create and demand the PrincipalPermission object
   6:     PrincipalPermission p = new PrincipalPermission(null, r, true);
   7:     p.Demand();
   8:  
   9:     Console.WriteLine("Access allowed.");
  10:     // TODO: Main application
  11: }
  12: catch (System.Security.SecurityException ex)
  13: {
  14:     Console.WriteLine("Access denied: " + ex.Message);
  15:     // TODO: Log error
  16: }

Metoda Demand sprawdza, czy użytkownik spełnia określone wymagania. Jeśli nie – zostanie wygenerowany wyjątek.

Ta metoda kontroli może zostać wykorzystana do ograniczenia fragmentu kodu, np. części metody.

Wynik programu, który powstał do tej pory. Uruchomiony jako zwykły użytkownik:

image

Uruchomiony jako administrator:

image

Kontrola deklaratywna

Kontrola deklaratywna ogranicza dostęp do całej metody. Realizowana jest za pomocą atrybutu (PrincipalPermissionAttribute) opisującego metodę:

   1: [PrincipalPermission(SecurityAction.Demand, Role = @"BUILTIN\Administratorzy")]
   2: //[PrincipalPermission(SecurityAction.Demand, Name = @"CONTOSO\User1", Role = @"CONTOSO\Managers")]
   3: [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
   4: static void AdministratorsOnlyMethod()
   5: {
   6:     Console.WriteLine("Admin is mighty!");
   7: }

Wywołanie w kodzie:

   1: try {
   2:     AdministratorsOnlyMethod(); 
   3: } catch (System.Security.SecurityException ex) {
   4:     Console.WriteLine("Your account lacks permission to that function.");
   5: }

 

Metody generyczne

Jeżeli chcemy stworzyć własny, prosty mechanizm uwierzytelniania, który oparty będzie na nazwie użytkownika, oraz typie uwierzytelnienia, możemy skorzystać z klas GenericIdentity i GenericPrincipal. Przykład wykorzystania:

   1: static void Main(string[] args)
   2: {
   3:     GenericIdentity myUser1 = new GenericIdentity("JHealy");
   4:     String[] myUser1Roles = new String[] { "IT", "Users", "Administrators" };
   5:     GenericPrincipal myPrincipal1 =
   6:     new GenericPrincipal(myUser1, myUser1Roles);
   7:     GenericIdentity myUser2 = new GenericIdentity("TAdams");
   8:     String[] myUser2Roles = new String[] { "Users" };
   9:     GenericPrincipal myPrincipal2 =
  10:     new GenericPrincipal(myUser2, myUser2Roles);
  11:     try
  12:     {
  13:         Thread.CurrentPrincipal = myPrincipal1;
  14:         TestSecurity();
  15:         Thread.CurrentPrincipal = myPrincipal2;
  16:         TestSecurity();
  17:     }
  18:     catch (Exception ex)
  19:     {
  20:         Console.WriteLine(ex.GetType().ToString() + " caused by " + Thread.CurrentPrincipal.Identity.Name);
  21:     }
  22: }
  23:  
  24: [PrincipalPermission(SecurityAction.Demand, Role = "IT")]
  25: private static void TestSecurity()
  26: {
  27:     Console.WriteLine(Thread.CurrentPrincipal.Identity.Name + " is in IT.");
  28: }

Podsumowanie

Autoryzacja i uwierzytelnianie z wykorzystaniem mechanizmów systemu operacyjnego daje nam dużo możliwości. W artykule przedstawiłem podstawowe metody pracy z tymi mechanizmami. Bardziej dociekliwi czytelnicy powinni zapoznać się z interfejsami IIdentity oraz IPrincipal. Implementując te interfejsy jesteśmy w stanie rozszerzyć domyślną funkcjonalność systemu.

Kolejny artykuł w serii to 70-536: Using Access Control Lists

Tagi: , , , ,

Comments (2) -

Rycu
Rycu Poland
1/3/2010 12:08:27 PM Permalink

Drobna ciekawostka, sam często z rozpędu tłumaczę authentication tak jak w poście(autentykacja), ale w języku polskim NIE WYSTĘPUJE słowo "autentykacja"(http://usjp.pwn.pl/lista.php?co=autentykacja). Poprawne tłumaczenie to "Uwierzytelnianie" :)

Pozdrawiam

kml
kml Poland
1/3/2010 1:45:23 PM Permalink

@Rycu: Dzięki za uwagę. Już poprawiłem błąd.

Pingbacks and trackbacks (2)+

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


Eastgroup.pl na facebooku