Acegi – Uwierzytelnianie i Autoryzacja w Springu
Acegi jest to narzędzie służące zapewnieniu bezpieczeństwa w aplikacjach korporacyjnych. Zapewnia wszechstronne mechanizmy uwierzytelniania i autoryzacji.
Uwierzytelnianie i Autoryzacja
Są to podstawowe elementy zapewnienia bezpieczeństwa w aplikacji sieciowej. Uwierzytelnianie polega na potwierdzeniu tożsamości użytkownika, zwykle poprzez podanie loginu i hasła. Autoryzacja oznacza przyznanie określonemu użytkownikowi dostępu do pewnych zasobów.
Przykładowa Aplikacja
Kod źródłowy przykładowej aplikacji możecie pobrać tu. Wersja bez Acegi jest dostępna tutaj.
Acegi
Działanie Acegi polega na zastosowaniu szeregu filtrów. Dbają one o to aby użytkownik uzyskał dostęp do tych elementów, do których ma prawo. Działanie rozpoczyna sprawdzenie czy dany zasób jest zabezpieczony. Następnie sprawdza się czy użytkownik jest już zalogowany, jeśli nie zostanie o to poproszony w tym momencie. Po poprawnym zalogowaniu sprawdza się czy jest możliwy dostęp do zasobu, a następnie go udostępnia.
Konfiguracja
FilterChainProxy
Do połączenia zestawu filtrów w spójnie działającą całość użyjemy FilterChainProxy. Obiekt ten zajmie się utworzeniem i przygotowaniem potrzebnych filtrów.
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter, formAuthenticationProcessingFilter,exceptionTranslationFilter, filterSecurityInterceptor</value> </property> </bean>
Parametr filterInvocationDefinitionSource zapewnia przygotowanie poprawnych adresów oraz definiuje listę filtrów do zestawienia. Teraz przejdźmy do konfiguracji poszczególnych elementów.
AutenticationProcessingFilter
<bean id="formAuthenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager" /> <property name="authenticationFailureUrl" value="/badlogin.jsp" /> <property name="defaultTargetUrl" value="/" /> <property name="filterProcessesUrl" value="/j_acegi_security_check" /> </bean>
Pierwszy filtr, do którego przyjdzie żądanie. Filtr ten zajmie się uwierzytelnieniem użytkownika poprzez wyświetlenie formularza z prośbą o podanie loginu i hasła, jednak nie zajmuje się potwierdzeniem ich poprawności. Te funkcje znajdziemy w kolejnych elementach.
HttpSessionContextIntegrationFilter
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"> </bean>
Proste zadanie tego bean’a polega na przekazywaniu informacji dotyczących uwierzytelnienia pomiędzy kolejnymi żądaniami.
ExceptionTranslationFilter
Zadaniem tego komponentu jest przechwytywanie wyjątków związanych z uwierzytelnianiem i autoryzacją. Jeśli użytkownik jest niezalogowany, wyświetlony zostanie ekran logowania. Jeśli użytkownik był już zalogowany, ale nie ma praw dostępu do danego zasobu, do przeglądarki zostanie zwrócony komunikat 403 Forbidden.
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint"> <ref bean="formLoginAuthenticationEntryPoint" /> </property> </bean>
FieldSecurityInterceptor
Bean zawierający informacje o zabezpieczonych zasobach. Ograniczenia dostępu definiujemy za pomocą ścieżek URL. Definiujemy je w objectDefinitionSource.
<bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager" /> </property> <property name="accessDecisionManager"> <ref bean="accessDecisionManager" /> </property> <property name="objectDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /secure/admin/*=ROLE_ADMIN /secure/user/*=ROLE_USER </value> </property> </bean>
W powyższym przykładzie widzimy zdefiniowane dwie ścieżki. Do każdej z nich została przypisana inna rola. Użytkownicy z posiadający dostęp do jednej z nich będą posiadali dostęp do jednej ścieżki.
AuthenticationManager
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref bean="daoAuthenticationProvider" /> </list> </property> </bean>
Jak widać konfiguracja samego managera zawiera jedynie źródło danych, potrzebnych do weryfikacji użytkownika. Źródłem danych może być JDBC lub LDAP. W tym przypadku pokażę jednak jak skorzystać z prostego źródła InMemoryDaoImpl. Pozwala ono definiować użytkowników w springowym xml’u. Poniżej konfiguracja:
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="userDetailsService"> <ref bean="userDetailsProvider" /> </property> </bean>
<bean id="userDetailsProvider" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> <property name="userMap"> <value> admin=acegi,ROLE_ADMIN user=security,ROLE_USER root=demo,ROLE_ADMIN,ROLE_USER </value> </property> </bean>
Mamy tu zdefiniowanych trzech użytkowników, jeden z nich ma przypisane dwie role. Będzie on miał dostęp do zasobów przeznaczonych dla każdej z nich.
AccessDecisionManager
Komponent odpowiedzialny za ustalenie czy dany użytkownik ma prawo dostępu do danego zasobu. Do podjęcia decyzji używany jest RoleVoter.
<bean id="accessDecisionManager" class="org.acegisecurity.vote.UnanimousBased"> <property name="decisionVoters"> <list> <ref bean="roleVoter" /> </list> </property> </bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"> <property name="rolePrefix"> <value>ROLE_</value> </property> </bean>
AuthenticationEntryPoint
Element odpowiedzialny za zdefiniowanie formularza logowania. Za jego pomocą możemy również wymusić przesyłanie loginu i hasła za pomocą https, ustawiając property forceHttps na true.
<bean id="formLoginAuthenticationEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl"> <value>/login.jsp</value> </property> </bean>
Ekran logowania
<form method="POST" action="<c:url value='j_acegi_security_check' />" > Username: <input type="text" name="j_username"><br> Password: <input type="password" name="j_password"><br> <input type="submit" > </form>
Podsumowanie
Jak widać przygotowanie uwierzytelniania za pomocą Acegi nie jest szczególnie skomplikowane. Praktycznie cała konfiguracja jest zawarta w Springowych plikach XML i nie powinna sprawić nikomu kłopotów. Polecam korzystanie z Acegi przy aplikacjach o małych i średnich wymogach bezpieczeństwa. Jeśli korzystacie jednocześnie z SiteMesh upewnijcie się, że filtry Acegi wywoływane są jako pierwsze.









September 28th, 2007 at 16:54
“Polecam korzystanie z Acegi przy aplikacjach o małych i średnich wymogach bezpieczeństwa” – a co z aplikacjami o dużych wymaganiach jeśli chodzi o bezpieczeństwo? Acegi nie jest dla takich aplikacji odpowiedni? Jeśli nie Acegi to co? Przede wszystkim co to znaczy “duże wymagania”? Chodzi o elastyczność/usability czy o odporność na ataki?
October 2nd, 2007 at 10:48
Swojego czasu słyszałem opinię, że Acegi sprawdza się najlepiej na prostszych serwerach aplikacji, aby uzyskać możliwość lepszego dostosowania do wymogów bezpieczeństwa lepiej sprawdzi się bardziej zaawansowany serwer i korzystanie z jego systemu kontroli dostępu.
November 25th, 2007 at 16:24
A można prosić o trochę więcej szczegółów? Albo o uzasadnienie twierdzenia? Jaki bardziej zaawansowany serwer masz na myśli?
May 19th, 2008 at 20:50
Witam
Zrobiłem applikację na podstawie tego przykładu SpringMVC, Velocity, konfigurację w dużej mierze oparłem na adnotacjach. Wszystko chula stronki są zabezpieczone. Mam pytanie jak zrobić aby po prawidłowym zalogowaniu stworzyć zmienną sesyjną np zawierającą login zalogowanego? Taki logi później bym wyświetlał na stronie.
May 20th, 2008 at 05:40
Aby uzyskać dostęp do danych uwierzytelnionego użytkownika możesz skorzystać z następującej konstrukcji:
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Powyższe wywołanie przy w miarę standardowej konfiguracji (z tego co pamiętam) zwróci obiekt klasy org.springframework.security.userdetails.User.