#16.2 — zabezpieczanie aplikacji z użyciem Spring Security

By 10 January 2015 February 6th, 2016 Kurs Javy

Dziś kole­j­na część z cyk­lu o Spring Secu­ri­ty — dodawanie i kon­fig­u­rac­ja w projekcie

Jed­nocześnie przepraszam bard­zo za opóźnie­nie, wiele się ostat­nio dzieje i nieste­ty ma to wpływ na pisanie na bieżą­co :( Obiecu­ję, że spróbu­je się oga­r­nąć i pisać na czas w przyszłości :)

Ale prze­jdźmy do rzeczy — kon­fig­u­racji Spring Secu­ri­ty w projekcie.

Lekcja

Dodawanie SpringSecurity do projektu

Zależności Maven

Aby dodać Spring Secu­ri­ty do pro­jek­tu, doda­je­my w pliku pom.xml zależnoś­ci do mod­ułów org.springframework.security:spring-security-config oraz org.springframework.security:spring-security-taglibs .

Uwa­ga! W momen­cie pisa­nia tego kur­su najnowsza wer­s­ja Spring Secu­ri­ty to 3.2.5, która współpracu­je ze Spring Frame­work w wer­sji 3.2.8 (jeśli będziesz kiedykol­wiek potrze­bowała to sprawdz­ić — zerknij na stronę z opisem arte­fak­tu — na dole strony zna­jdziesz arte­fak­ty, z których korzys­ta wraz z wer­s­ja­mi, które są uży­wane). Z tego powodu do cza­su wyda­nia wer­sji 4.0.0, konieczne jest obniże­nie wer­sji Spring Frame­work w plikach pom.xml do 3.2.8.RELEASE oraz spring-data do wer­sji 1.5.2.RELEASE (wer­s­ja zgod­na ze Spring Frame­work 3.5.8). W przyszłoś­ci po wyda­niu wer­sji, ta lekc­ja zostanie zaktualizowana.

Konfiguracja XML — uproszczona

W następ­nym kroku, do kat­a­logu /src/main/resources doda­je­my plik security-context.xml o następu­jącej treści:

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <http auto-config="true">
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="kobietydokodu" password="jakieshaslo" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

Uwa­ga! Powyższy plik zaw­iera staty­czną listę użytkown­ików wraz z ich hasła­mi — w dal­szej częś­ci zamien­imy to w taki sposób, aby korzys­tał z utwor­zonej przez nas bazy danych. Niem­niej łatwiej jest skon­fig­urować i rozwiązy­wać ewen­tu­alne prob­le­my w takiej kon­fig­u­racji — z tego powodu będziemy korzys­tali najpierw ze staty­cznej listy użytkown­ików, którą następ­nie pod­mien­imy na dynam­iczną, z uży­ciem bazy danych.

Dodal­iśmy dwa ele­men­ty — pier­wszy pozwala nam na uproszc­zoną kon­fig­u­rację (atry­but auto-config=”true”), dzię­ki czemu dosta­je­my automatycznie:

  • for­mu­la­rz logowania
  • oper­ac­je wylogowywania
  • możli­wość uwierzytel­ni­a­nia za pomocą nagłówków pro­tokołu HTTP (nie będziemy z tego korzys­tać, ale może się to przy­dać np. jeśli tworzymy API)

Konfiguracja web.xml

W pliku tym dokonu­je­my 2 zmi­an — po pier­wsze, mody­fiku­je­my poniższy fragment:

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

Wskazu­jąc ter­az lokaliza­cję naszego pliku security-context.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/classes/security-context.xml</param-value>
</context-param>

Spowodu­je to, że plik ten będzie tzw. głównym kon­tek­stem (więcej na ten tem­at możesz poczy­tać w lekcji 9) i będzie doty­czył wszys­t­kich innych kon­tek­stów (w tym naszej aplikacji webowej).

Pon­ad­to doda­je­my poniższe elementy:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Powodu­ją one, że wszys­tkie zapy­ta­nia HTTP będą ‘wery­fikowane’ za pomocą Spring Security.

W tym miejs­cu warto upewnić się, czy wszys­tko dzi­ała poprawnie — czy może­my uru­chomić naszą aplikację oraz czy po wejś­ciu na adres (adres ser­w­era i kon­tek­stu aplikacji)/spring_security_login, może­my się zal­o­gować za pomocą podanych wcześniej loginu i hasła.

Korzystamy z użytkowników w bazie danych

Ten krok poz­woli nam logować się za pomocą danych użytkown­ików z bazy danych — a więc jak w prawdzi­wej, komer­cyjnej aplikacji :) W tym miejs­cu zakładamy, że samodziel­nie utworzyłaś w swo­jej aplikacji funkcjon­al­ność rejes­tracji — czyli tak naprawdę for­mu­la­rz oraz klasy z adno­tac­ja­mi JPA pozwala­jące dodawać użytkown­ików do bazy danych (jeśli nie jesteś pew­na, jak to zro­bić, przy­pom­nij sobie z lekcji 10 (o tworze­niu for­mu­la­rzy i odbiera­niu danych) oraz 14 (o uży­wa­niu bazy danych w aplikacji Spring MVC) ). Alter­naty­wą jest ręczne utworze­nie reko­rdów w bazie danych, ale praw­ie każ­da aplikac­ja potrze­bu­je możli­wość rejes­tracji się przez użytkown­ików, więc lep­iej zro­bić to od razu.

Pier­wszym krok­iem jest prze­niesie­nie połączenia z bazą danych z pliku applicationContext.xml do pliku security-context.xml . Przenosimy więc cały poniższy tag z jed­nego pliku do drugiego (oczy­wiś­cie, w Two­jej aplikacji mogą być to nieco inne dane):

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/koty" />
    <property name="username" value="login" />
    <property name="password" value="haslo" />
</bean>

W prze­ci­wnym wypad­ku Spring Secu­ri­ty nie ‘widzi­ało’ by połączenia z bazą danych, a jest ono potrzeb­ne do wery­fikacji loginu i hasła.

Kole­jnym krok­iem jest pod­mi­ana staty­cznej listy użytkown­ików na taką, która obsługu­je bazy danych. W pliku security-context.xml zamieni­amy więc fragment:

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="kobietydokodu" password="jakieshaslo" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

Na następu­ją­cy:

<authentication-manager>
   <authentication-provider>
 <jdbc-user-service data-source-ref="dataSource"
   users-by-username-query=
     "SELECT username, password, enabled FROM users WHERE username=?"
   authorities-by-username-query=
     "SELECT username, ‘ROLE_USER’ FROM users WHERE username =?  " />
   </authentication-provider>
 </authentication-manager>

W tym frag­men­cie dzieją się następu­jące rzeczy:

  • tworzymy jdbc-user-ser­vice — jest to gotowa imple­men­tac­ja, która pozwala obsługi­wać uwierzytel­ni­an­ie z uży­ciem bazy danych ustaw­ia­jąc jedynie kil­ka niezbęd­nych atrybutów
  • wskazu­je­my połacze­nie do bazy danych (atry­but data-source-ref=”dataSource” — wskazu­je nam na beana o id data­Source — tego, którego w poprzed­nim kroku prze­nieśliśmy do naszego pliku security-context.xml)
  • ustaw­iamy zapy­tanie SQL, które pobierze nam użytkown­i­ka, hasło oraz infor­ma­c­je, czy użytkown­ik jest akty­wny dla podanego loginu (atry­but users-by-user­name-query) — dzię­ki temu Spring pobierze te dane i zwery­fiku­je, czy podane login i hasło są prawidłowe
  • ustaw­iamy zapy­tanie SQL, które dla określonego użytkown­i­ka pobierze nam listę ról, które on posi­a­da — ponieważ w naszej aplikacji nie będziemy korzys­tać z ról (powiemy o nich w osob­nym wpisie jako mate­ri­ał rozsz­erza­ją­cy w przyszłoś­ci), podane zapy­tanie pobiera staty­czny ciąg znaków ‘ROLE_USER’ dla wybranego użytkown­i­ka (jest to uproszc­zone pode­jś­cie, które nie do koń­ca jest poprawnym, ale na ten moment będziemy z niego korzys­tać w celu pokaza­nia idei)

Oczy­wiś­cie w zależnoś­ci od tego, jak wyglą­da Two­ja baza, być może będziesz musi­ała zmody­fikować zapy­ta­nia SQL tak, aby odpowiadały Twoim naz­wom tabel i pól. Powyższy przykład moż­na bezpośred­nio użyć np. z tabelą przed­staw­ioną na poniższym diagramie:

model

Kod SQL do utworzenia takiej tabeli zna­jdziesz poniżej:

CREATE TABLE IF NOT EXISTS `users` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(45) NOT NULL,
  `password` VARCHAR(63) NOT NULL,
  `enabled` TINYINT(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`))
ENGINE = InnoDB

Tak skon­fig­urowana aplikac­ja powin­na poz­wolić na logowanie się za pomocą nazwy użytkown­i­ka i hasła zapisanych w bazie danych.

PS. Oczy­wiś­cie musisz jeszcze dodać reko­rd do bazy danych, możesz do tego użyć poniższego zapy­ta­nia SQL:

INSERT INTO users (`username`, `password`, `enabled`) VALUES ('kobietydokodu', 'jakieshaslo', 1);

Podsumowanie

W tej lekcji nauczyliśmy się dodawać Spring Secu­ri­ty do naszego pro­jek­tu — w kole­jnej częś­ci będziemy zabez­pieczać poszczególne czyn­noś­ci w naszej aplikacji, korzys­tać z infor­ma­cji o zal­o­gowanym użytkown­iku i wyświ­et­lać treś­ci w zależnoś­ci od tego, kim jest zal­o­gowana oso­ba. Dodamy też włas­ny for­mu­la­rz logowa­nia zami­ast domyślnego.

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!