#16 — zabezpieczanie aplikacji z użyciem Spring Security

By 14 December 2014 Kurs Javy

Dzisiejsza lekc­ja będzie podzielona na dwie częś­ci, w których powiemy sobie o zabez­piecza­niu aplikacji z uży­ciem Spring Security.

W tej lekcji omówimy kil­ka mech­a­nizmów i pojęć, niezbęd­nych do zrozu­mienia zagad­nień związanych z zabez­pieczaniem aplikacji. W drugiej częś­ci dodamy pewne ograniczenia do naszej aplikacji.

Lekcja

Sesja, ciasteczka itp — czyli jak to działa

Protokół HTTP (bezstanowy)

Pro­tokół HTTP jest pro­tokołem bezs­tanowym, tzn. dwa zapy­ta­nia do ser­w­era od jed­nego klien­ta (przeglądarki/użytkownika) jed­no po drugim nie są w żaden sposób ze sobą ‘pow­iązane’ — tj. ser­w­er nie jest w stanie określić, czy pochodzą od tego samego użytkown­i­ka czy nie (mogą być np. z 2 urządzeń, które dzi­ała­ją w tej samej sieci, np. w jed­nym biurze).

Aby temu zaradz­ić, stwor­zony został pewien mech­a­nizm, tzw. cook­ies — cook­ies to małe pli­ki, które mogą prze­chowywać proste infor­ma­c­je (klucz-wartość), dostęp­ne tylko dla danej strony inter­ne­towej (np. inna strona inter­ne­towa nie jest w stanie odczy­tać ciasteczek Two­jej aplikacji webowej — ma to na celu zwięk­sze­nie bez­pieczeńst­wa, choć cza­sem zdarza się, że w przeglą­dark­ach inter­ne­towych są pewne luki). Ciastecz­ka z kolei są wyko­rzysty­wane do obsłu­gi mech­a­niz­mu sesji poprzez zapisanie w ciasteczku tzw. iden­ty­fika­to­ra sesji — unikalnego ciągu znaków, który w połącze­niu z adresem IP pozwala jed­noz­nacznie ziden­ty­fikować użytkown­i­ka strony i pow­iązać go z zapy­ta­ni­a­mi, które wykonu­je na serwerze.

Sesja i obsługa w Springu

Ses­je dzi­ała­ją trochę podob­nie jak wspom­ni­ane ciastecz­ka — pozwala­ją prze­chowywać pary klucz-wartość unikalne dla danego użytkown­i­ka. Mówiąc w kon­tekś­cie języ­ka Java, moż­na je porów­nać do obiek­tu typu Map<String, Object>, który jest unikalny dla każdego fizy­cznego użytkown­i­ka. Różni­ca pomiędzy sesją a ciasteczkiem jest taka, że o ile ciastecz­ka są prze­chowywane po stron­ie klien­ta (np. w przegladarce), ses­ja jest prze­chowywana na ser­w­erze (użytkown­ik nie ma więc możli­woś­ci manip­u­lowa­nia nią bezpośred­nio), jest też bardziej elasty­cz­na (ciastecz­ka pozwala­ją prze­chowywać pary klucz-wartość, gdzie zarówno klucz jak i wartość to ciąg znaków, w przy­pad­ku sesji wartość może być dowol­nym obiektem).

W Springu z sesji może­my korzys­tać bezpośred­nio — np. doda­jąc jako argu­ment naszej metody w kon­trol­erze obiekt typu HttpSes­sion, np w ten sposób:

@Controller
public class Kontroler {
    @RequestMapping(“/”)
    public String zrobCos(HttpSession session) {
        //...
    }
}

Dru­gi sposób to korzys­tanie z sesji nie wprost — np. uży­wa­jąc bib­liote­ki opartej o mech­a­nizm sesji (np. Spring Secu­ri­ty, o którym dzisi­aj będziemy roz­maw­iać) lub tworząc beany o tzw. zasięgu sesyjnym.

Zasięg sesyjny

Pojęcie zasięgu

W springu ist­nieje poję­cie zasięgu danego beana (kom­po­nen­tu). Oznacza on, w jakim kon­tekś­cie jest on ‘unikalny’ — domyśl­ny zasięg to sin­gle­ton, co oznacza, że w całej aplikacji mamy tylko jeden taki obiekt. W naszej aplikacji jed­nak częs­to jest potrze­ba utworzenia kom­po­nen­tu, który będzie pow­iązany z użytkown­ikiem i widoczny tylko w jego ‘kon­tekś­cie’. Do tego właśnie może­my użyć zasięgu sesyjnego.

Zasięgi w Spring’u

Aby ustaw­ić konkret­ne­mu beanowi (np. ser­wisowi lub dao jeśli jest taka potrze­ba) odpowied­ni zasięg, uży­wamy adno­tacji @Scope( zasieg ), gdzie zasieg to jeden z poniższych:

  • ConfigurableBeanFactory.SCOPE_SINGLETON — domyśl­na wartość, jest tylko jeden obiekt tego typu w całej aplikacji
  • ConfigurableBeanFactory.SCOPE_PROTOTYPE — za każdym razem, kiedy ten bean jest uży­wany, twor­zony jest nowy obiekt (ale uwa­ga, ‘uży­wany’ tzn np. podłączany przy uży­ciu adno­tacji @Autowired do innego beana, a nie za każdym razem, kiedy korzys­tamy z jego metod; inny­mi słowy, za każdym razem, kiedy pobier­any jest on z kon­tek­stu Spring’a)
  • WebApplicationContext.SCOPE_REQUEST — twor­zony jest osob­ny obiekt do obsłu­gi każdego osob­ne­go zapy­ta­nia HTTP (czyli przy wyświ­etle­niu każdej strony)
  • WebApplicationContext.SCOPE_SESSION — twor­zony jest dla każdej sesji, czyli najczęś­ciej dla każdego jed­nego użytkown­i­ka korzys­ta­jącego z aplikacji w danym momencie
Zasięg sesyjny

Zasięg sesyjny jest dla nas najbardziej intere­su­ją­cy na ten moment i na nim się skupimy. W prak­tyce do tej pory nie miałem okazji korzys­tać za częs­to z innych zakresów poza sesyjnym, były to dość specy­ficzne przy­pad­ki i wyda­je mi się, że na początku swo­jej kari­ery raczej nie spotkasz się z podob­ną sytuacją :)

Beana w zasięgu sesyjnym może­my utworzyć np. w celu prze­chowywa­nia danych użytkown­i­ka w wygod­niejszej formie (Spring Secu­ri­ty ofer­u­je taką możli­wość, ale korzys­tanie z tego obiek­tu może być dość prob­lematy­czne). Co ważne, ses­ja (a więc także beany o takim zasięgu nie są twor­zone w momen­cie zal­o­gowa­nia, a w momen­cie pier­wszego zapy­ta­nia HTTP do naszej aplikacji (np. wejś­cia na stronę główną)! Dlat­ego ważne jest, aby w beanach tego typu uwzględ­ni­ać także możli­wość, że użytkown­ik nie jest zalogowany.

Spring security

Spring Secu­ri­ty to bib­liote­ka rozwi­jana wraz z frame­workiem (choć może być uży­wana nieza­leżnie od niego), która pozwala na bard­zo elasty­czną kon­fig­u­rację za pomocą bard­zo małej iloś­ci kodu (właś­ci­wie to XMLa).

Główny kon­cept Spring Secu­ri­ty opiera się o fil­try, czyli każde zapy­tanie prze­chodzi przez zestaw ‘kry­ter­iów’ z których każde może zde­cy­dować o tym, czy odmówić dostępu, udzielić go, lub przekazać decyzję dalej. W połącze­niu z bard­zo sze­roką gamą gotowych kom­po­nen­tów i kon­fig­u­racji może­my szy­bko i sprawnie umożli­wić np. logowanie za pomocą Face­booka czy też zwykłego loginu i hasła.

Spring Secu­ri­ty obe­j­mu­je dwa zagad­nienia — uwierzytel­ni­an­ie i auto­ryza­cję. Pier­wszy z nich, to inny­mi słowy potwierdze­nie, że ktoś jest osobą za którą się poda­je. Może to być w formie poda­nia loginu i hasła, zal­o­gowa­nia przez Face­book czy Google+ itp. Drugie, to określe­nie, czy zal­o­gowany (uwierzytel­niony) użytkown­ik ma pra­wo wykon­ać daną czyn­ność (pokazać szczegóły ele­men­tu, usunąć ele­ment itp).

Spring Secu­ri­ty pozwala na bard­zo pre­cyzyjne i wygodne zarządzanie bez­pieczeńst­wem aplikacji — może­my przede wszys­tkim określić reguły dla całych wzor­ców URL (np. powiedzieć, że poza stroną logowa­nia oso­by nieza­l­o­gowane nie mogą nic wyświ­etlić) ale też dla poje­dynczych metod (czyli np. ta strona może być wyświ­et­lona tylko admin­is­tra­torom). Tymi dwoma sposoba­mi będziemy się zaj­mowali w drugiej częś­ci tej lekcji. Warto jed­nak wiedzieć, że Spring Secu­ri­ty może też więcej — np. z kolekcji obiek­tów zwracanej z jakiejś metody usunąć te, do których użytkown­ik nie ma uprawnień lub sprawdz­ić, czy wywołanie metody jest możli­we przez użytkown­i­ka aku­rat dla podanych argumentów!

O samym Spring Secu­ri­ty moż­na by napisać naprawdę sporo i sporo już zostało napisane — dla osób ciekawych (a także dla tych, które chcą/potrzebują zrozu­mieć zasadę dzi­ała­nia lub zmody­fikować coś) gorą­co pole­cam książkę Pro Spring Secu­ri­ty autorstwa Car­lo Scarioni

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!