28 stycznia 2009

Bezpieczeństwo w Grails - rozdział 7 z "Beginning Groovy and Grails"

Kolejny rozdział 7. "Security in Grails" w książce Beginning Groovy and Grails: From Novice to Professional omawia dostępne wtyczki grailsowe umożliwiające wprowadzenie mechanizmów bezpieczeństwa w aplikacjach grailsowych. Możemy przeczytać o sposobie zabezpieczania danych użytkownika przed ich odczytaniem przez innych użytkowników (bezpieczeństwo na poziomie modelu - ang. domain/model-level security for a user), możliwości przypisania zbioru stron dla niektórych użytkowników, np. administratora (ang. administrator vs regular user access) oraz proste uwierzytelnianie z użyciem Basic Authentication. Nigdy nie spotkałem się z takim podziałem, ale też nigdy aspekt bezpieczeństwa nie był przeze mnie jakoś specjalnie badany. Gdzieś tam się po prostu przewijał. Poza ostatnim typem z uwierzytelnianiem z Basic Auth, który będzie poruszany w rozdziale 9, pozostałe dwa są tematem tego rozdziału. Omówiono wdrożenie bezpieczeństwa przy użyciu kilku różnych sposobów uwierzytelniania i autoryzacji - od najbardziej pracochłonnego i trywialnego - własnego rozwiązania, przez JSecurity, CAS (jedynie uwierzytelnianie), aby docelowo skorzystać ze Spring Security (dawniej Acegi Security). Żaden z nich nie jest rozwiązaniem dedykowanym dla Grails - za pomocą mechanizmu wtyczek udostępnione są funkcjonalności istniejących, zewnętrznych projektów.

Uwierzytelnianie (ang. authentication) to po prostu "przedstawienie się", np. za pomocą pary login/hasło lub certyfikatu. Autoryzacja (ang. authorization) to wydanie zgody na skorzystanie z zasobu.

Przechowywanie haseł zazwyczaj związane jest z zakodowaniem ich z pomocą klasy użytkowej org.apache.commons.codes.digest.DigestUtils, która udostępnia metody dla różnych rodzajów funkcji haszujących - MD5, SHA.

Jednym z ważniejszych katalogów Grails w kontekście konfiguracji bezpieczeństwa jest katalog konfiguracyjny grails-app/conf z m.in. Bootstrap.groovy. Dzięki Bootstrap.groovy Grails umożliwia wypełnienie danymi bazę danych lub...kontekst aplikacji webowej (!) Dodatkowo, podział konfiguracji według środowiska uruchomieniowego - rozwojowe, produkcyjne, testowe - pozwala na uruchomienie pewnych funkcjonalności wyłącznie w danym środowisku. Wykrycie środowiska następuje przez sprawdzenie wartości grails.util.GrailsUtil.environment, np. "development", "production". Wystarczy połączyć to z switch/case.

W ramach uwierzytelniania możemy dodatkowo skorzystać z mechanizmu CAPTCHA (ang. Completely Automated Public Turing Test to Tell Computers and Humans Apart) - na serwerze tworzony jest obrazek, na którym znajduje się ciąg przypadkowych znaków, które użytkownik będzie proszony o wpisanie. W ruch idzie wtyczka Simple Captcha Plugin. Istnieje jednak bardziej zaawansowana wtyczka jcaptcha, która poza utworzeniem obrazka pozwala na dołączenie pliku wav. Wymaga więcej wysiłku podczas konfiguracji i autorzy jedynie wspominają o niej. Wywołanie wtyczki captcha to
 <img src="${createLink(controller: 'captcha', action: 'index')}" />
który czytamy - dołącz obrazek utworzony przez kontroler CaptchaController metodą index. Wystarczy więc zajrzeć do odpowiedniego pliku CaptchaController.groovy i przekonać się, w jaki sposób możliwe jest przesyłanie nie tylko strumienia HTML, ale również binarnego, np. zdjęcia. CaptchaController zapisuje użyty ciąg znaków w sesji, jako session.captcha. Niestety, porównanie wprowadzonego przez użytkownika ciągu z tym utworzonym przez CAPTCHA jest na naszych barkach. Coś w stylu (Listing 7-2):
 def captchaText = session.captcha
if (params.captcha.toUpperCase() == captchaText) {
// poprawnie wprowadzony ciąg znaków
...
}
Kolejnym krokiem jest wykorzystanie filtrów, które w Grails nie wymagają żadnych wpisów do WEB-INF/web.xml (nie zapomnieliśmy, że Grails to w końcu aplikacja webowa, więc taki plik *musi* istnieć). Wystarczy stworzyć klasę zakończoną Filters w grails-app/conf z metodą filters. O filtrach pisałem już w relacji Tworzenie interfejsu użytkownika w Grails - rozdział 5 z "Beginning Groovy and Grails". W filtrach dostępne są obiekty controllerName, request, response, session, servletContext, applicationContext oraz params.

JSecurity jest najprostszym z rozwiązań wprowadzającym pojęcia rola (ang. role) oraz uprawnienie (ang. permission). Jest prawdopodobnie bardzo podobny do tego, który sami stworzylibyśmy, jednakże jego zaletą jest to, że już istnieje i nie wymaga skomplikowanej konfiguracji. JSecurity jest zewnętrznym projektem, który ma swoją wtyczkę grailsową jsecurity. Może korzystać z różnych źródeł danych do obsługi uwierzytelniania (w książce korzysta się wyłącznie z bazy danych). Instalujemy wtyczkę poleceniem grails install-plugin jsecurity, a następnie tworzymy przestrzeń bezpieczeństwa (ang. realm) oraz klasy dziedzinowe wraz z kontrolerami i stronami GSP poleceniem grails create-db-realm. Wykonanie polecenie to również założenie katalogu grails-app/realms, w którym klasy zakończone na Realm są definicją źródeł danych do uwierzytelniania i autoryzacji użytkowników, np. względem bazy danych czy LDAP.

Wtyczka grailsowa cas-client umożliwa wykorzystanie JA-SIG Central Authentication Service (CAS). Jest to nic innego jak klient grailsowy oparty na kliencie javowym dostarczanym w CAS. Wdrożenie CASa pozwala na centralne zarządzanie użytkownikami i ich uwierzytelnianie nie tylko w aplikacjach javowych. Instalacja wtyczki to grails install-plugin cas-client. Konfigurację danych serwera CAS znajdziemy w grails-app/conf/Config.groovy. Podczas budowania aplikacji grailsowej konfiguracja CAS trafia do WEB-INF/web.xml docelowej aplikacji webowej. Parametry grailsowe rozpoczynające się na cas.* zamieniane są na odpowiednie parametry edu.yale.its.tp.cas.client.filter.*, poza cas.urlPattern oraz cas.disabled, które są specyficzne dla wtyczki grailsowej. Kontrolery zabezpieczamy filtrami lub rozszerzając własną klasę z odpowiednimi wywołaniami, np. sprawdzając, czy użytkownik ma prawo do wykonania danej operacji. Nazwa użytkownika dostępna jest w atrybucie sesyjnym CASFilter.CAS_FILTER_USER.

Jako ostatnie rozwiązanie prezentowany jest Spring Security (dawniej Acegi Security) w postaci wtyczki grailsowej acegi. Mamy do dyspozycji uwierzytelnianie z LDAP, CAS, JAAS oraz CAPTCHA. Jako to ujęli autorzy: "Grails is in many ways a glorified Spring application", więc skoro Grails to Spring Framework, więc w kontekście bezpieczeństwa nie możemy postawić na nic innego niż właśnie na Spring Security. Instalacja typowa - grails install-plugin acegi. Następnie wykonujemy grails create-auth-domains, która tworzy klasy dziedzinowe Person, Authority oraz Requestmap. Możemy utworzyć dodatkowe kontrolery i widoki poleceniem grails generate-manager, ale nie jest to konieczne, jeśli samodzielnie zamierzamy zarządzać modelem. Requestmap to zbiór informacji o zabezpieczonych stronach i jakie uprawnienia są wymagane. Dane testowe możemy utworzyć przez odpowiednie konstrukcje GORMowe na klasach dziedzinowych w Bootstrap.groovy. Konfiguracja wtyczki jest w grails-app/config/AcegiConfig.groovy. Zabezpieczanie stron możliwe jest z użyciem dedykowanych znaczników GSP, m.in. g:isLoggedIn oraz g:isNotLoggedIn. Po uwierzytelnieniu wtyczka acegi umieszcza obiekt użytkownika w sesji, więc możemy odczytać jego dane na stronie GSP, np. (Listing 7-12):
 <g:isLoggedIn>
<b>${session.user?.firstName} ${session.user?.lastName}</b>
</g:isLoggedIn>
Kolejny rozdział to technologie Web 2.0 w Grails. Przed nami ponownie cała masa wtyczek, więc przy okazji poznawania Grails można poznać wiele projektów wokół (pewnie nie będę oryginalny, jeśli powiem, że o wielu nie słyszałem wcześniej).

2 komentarze:

  1. Acegi chyba jednak nie umieszcza użytkownika w sesji, tak jak sugerujesz, przytoczony kod (Listning 7-12) nic nie wyświetla.
    Aby wyświetlić informacje o użytkowniku należałoby raczej użyć znacznika <g:loggedInUserInfo />:
    <g:loggedInUserInfo field="firstName"/> <g:loggedInUserInfo field="lastName"/>
    Informacje są wyświetlane tylko, jeśli użytkownik jest zalogowany, tak więc użycie <g:isLoggedIn /> staje się zbędne (w tym przypadku)
    http://www.grails.org/AcegiSecurity+Plugin+-+Artifacts

    OdpowiedzUsuń
  2. Racja! Dzięki Marek za poprawkę. Siedzę z Spring Security przy innej technologi, więc (całkiem przypadkiem) znacznie łatwiej mi teraz zrozumieć temat i Twoją uwagę. Niedługo wrócę do wtyczki acegi w Grails. Jeszcze trochę... ;-)

    OdpowiedzUsuń