Přejít k navigační liště

Zdroják » Různé » Java na webovém serveru: autorizace a autentizace

Java na webovém serveru: autorizace a autentizace

Články Různé

Po předchozím díle o lokalizaci a formátování přistoupíme k dalšímu důležitému tématu. Tím je bezpečnost. Ukážeme si, jak v Javě autorizovat a autentizovat uživatele a jak jim umožnit přístup jen tam, kam ho mít mají. Také naše výuková aplikace trochu pokročila – umí přidávat záznamy o podnicích do databáze.

Pro začátek neuškodí zopakovat si dva základní pojmy – aneb „autorizace je když…“

  • Autentizace je operace, při které zjišťujeme totožnost subjektu.
  • Autorizace je operace, při které zjišťujeme, jestli je subjekt oprávněn k nějaké činnosti, např. přístup k objektu.

Abychom mohli rozhodnout, zda je subjekt oprávněn (autorizovat ho), musíme vědět, s kým máme tu čest – logicky tedy autorizaci předchází autentizace. Jako mnemotechnická pomůcka vám může posloužit: Autentizace – ptáme se: ,,Kdo to je?“, odpověď je ten. Autorizace – ptáme se: ,,Co může dělat?“, odpověď je to.

Jen pro připomenutí: jako obvykle si z Mercurialu stáhneme aktuální verzi zdrojových kódů k dnešnímu dílu seriálu:

$ hg pull
$ hg up "6. díl"

Případně si je můžete stáhnout jako bzip2 archiv přes web.

Možnosti zabezpečení webové aplikace

Stejně jako v případě práce s databázemi i v oblasti bezpečnosti je více cest, jak na věc jít. Můžete si vše řešit po svém, napsat si vlastní HTML přihlašovací formuláře, odesílat jméno a heslo POSTem třeba na servlet nebo JSP stránku, v ní údaje ověřit, nastavit jméno uživatele do nějakého proměnného sezení… Také můžete svoji aplikaci zabezpečit pomocí Filtrů ( javax.servlet.Filter), které aplikaci „překryjí“ a postarají se o ověření uživatelů a zabránění přístupu nezvaným hostům.

Jelikož ověřování uživatelů a věci s tím spojené musí řešit prakticky každá aplikace, nedává moc smysl, aby si je vývojář psal s každou aplikací znovu a znovu. Dnešní díl bude tedy o tom, co nám Java jako platforma nabízí a jak vyřešit autorizaci/au­tentizaci bez psaní zbytečného kódu (jen s trochou konfigurace).

Kromě ušetřeného kódu je hlavní výhodou tohoto přístupu modularita a pružnost. Chcete mít uživatele v LDAPu místo v databázi? Není problém, stačí upravit konfiguraci a do aplikace není potřeba zasahovat. Rozhodli jste se, že místo HTTP autentizace chcete používat HTML formuláře? Opět – jen dva řádky v konfiguračním souboru – aplikaci není potřeba měnit. Díky tomu se můžete soustředit na smysl vaší aplikace (čím bude užitečná svým uživatelům), zatímco režii a servisní záležitosti za vás bude řešit platforma.

Definice bezpečnostních omezení na webu

Ve webové aplikaci si můžeme definovat místa (cesty), která budou chráněná a přístupná jen vybraným uživatelům, resp. uživatelským rolím. V souboru web.xml si zabezpečíme cestu/sprava/* , což je místo, kam časem umístíme administrační rozhraní aplikace.

<security-constraint>
    <web-resource-collection>
    <web-resource-name>Správa Nekuřák.net</web-resource-name>
        <url-pattern>/sprava/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
    <role-name>opravneny</role-name>
    </auth-constraint>
</security-constraint>

Říkáme zde, že autorizovaným k dané části webu je jen uživatel s rolí  opravneny.

Přihlašování a ověřování uživatelů

Rozhraní pro správu jsme zabezpečili – aplikační server zjistí, že uživatel nemá příslušnou roli a odepře mu přístup. Jenže chudák uživatel zatím nemá jak tuto roli získat – musíme mu umožňit prokázat jeho totožnost – autentizovat se. Opět budeme upravovat konfiguraci ve web.xml  – doplníme:

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>nekurakNET</realm-name>
</login-config>

Poznámka: Všimněte si prosím, že definice omezení ( security-constraint) a nastavení přihlašování ( login-config) jsou dvě různé věci – a do jisté míry na sobě nezávislé.

Odkazujeme se zde na tzv. realm (doména, království, říše), což je databáze uživatelských jmen, hesel a skupin, do kterých uživatelé patří. Jedná se o „databázi“ v širším slova smyslu – může to být prakticky cokoli – soubor na disku, LDAP, relační databáze, PKI… můžeme si i implementovat vlastní.

Výše uvedený kousek XML aplikačnímu serveru říká: „pokud neznámý uživatel přijde někam, kam nesmí (viz security-constraint výše), vyžádej si od něj jméno a heslo pomocí HTTP autentizace ( BASIC) a pokus se ho ověřit“.

Jelikož odkazovaná doména zatím neexistuje, veškeré pokusy o přihlášení budou neúspěšné. Doménu si tedy definujeme v aplikačním serveru. V případě Glassfishe to lze i jednoduše přes webové rozhraní:

Java - autentizace

Použili jsme FileRealm, což znamená, že informace o uživatelích a skupinách budou uloženy v obyčejném souboru na disku. Pro vývoj aplikace nám to v tuto chvíli postačí a později se můžeme snadno „přepnout“ a uživatele ověřovat např. oproti databázi.

Pomocí „Manage Users“ si přidáme uživatele a nastavíme mu heslo:

Java - autentizace

Všimněte si přiřazení uživatelských skupin (v našem případě jedna: spravce). Skupiny můžeme definovat buď pro celou doménu, nebo pro jednotlivé uživatele.

Nastavovat úložiště uživatelů na úrovni aplikačního serveru místo v aplikaci má hned dvě výhody: jednak můžeme jednu doménu sdílet mezi více aplikacemi, a jednak a můžeme aplikaci beze změn nasazovat na různé stroje a vždy se budou uživatelé ověřovat vůči správné doméně – jedná se tedy o stejnou výhodu jako u datových zdrojů definovaných na úrovni aplikačního serveru.

Zaujaly vás možnosti Javy a chcete se dozvědět o tomto jazyce víc? Akademie Root nabízí školení Základy programovacího jazyka Java a Pokročilejší kurz jazyka Java, na nichž se naučíte, jak tento multiplatformní objektově orientovaný jazyk používat.

Mapování rolí

Po úspěšném ověření v doméně má uživatel přiděleny určité skupiny (v našem případě spravce). Na úrovni aplikace ale pracujeme s rolemi (v našem případě opravneny). Mapování ze skupin na role provedeme v souboru sun-web.xml takto:

<security-role-mapping>
    <role-name>opravneny</role-name>
    <group-name>spravce</group-name>
</security-role-mapping>

V naší jednoduché aplikaci, kde mapujeme skupinu a roli 1:1, vám asi připadá, že je to práce navíc. Ale ve chvíli kdy budete potřebovat propojit složitější systémy, sdílet jednu doménu mezi více různými aplikacemi, pravděpodobně oceníte pružnost tohoto řešení. Na role se také odkazujete ze zdrojového kódu a určitě se vám je nebude chtít přejmenovávat a znovu kompilovat aplikaci. (zatímco upravit XML konfiguraci – na jednom místě – je hračka).

Poznámka: uživatelské role jsou důležité a nelze je brát jako „něco navíc“. Uživatel, který se úspěšně prokáže svým jménem a heslem, ještě nemá zaručeno, že se k chráněným zdrojům (v našem případě /sprava/*) dostane, pokud totiž nebude v příslušné skupině (a tím pádem nebude mít příslušnou roli), obdrží od serveru odpověď HTTP Status 403 - Access to the requested resource has been denied ve chvíli, kdy se bude pokoušet dostat, kam nemá (přestože jméno a heslo zadal správné).

HTTP vs. formulářová autentizace

Zatím jsme si ukázali jednoduchou HTTP autentizaci, což pro uživatele znamená, že jméno a heslo zadává do dialogového okna, které je součástí jeho prohlížeče. Na jednu stranu je to standardizované řešení, ovšem zase neumožňuje přizpůsobení – uživatel zadává údaje do nějakého šedivého okna, které navíc v každém prohlížeči vypadá jinak  – proto často chceme, aby uživatel mohl údaje vyplnit do HTML formuláře, který je součástí naší stránky.

Na rozdíl od jiných platforem to v Javě není žádná drastická změna – nebudeme muset zahodit to, co jsme doteď napsali a nebudeme muset programovat nic navíc. Pouze upravíme konfiguraci ve web.xml na:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>nekurakNET</realm-name>
    <form-login-config>
    <form-login-page>/?akce=prihlaseni</form-login-page>
    <form-error-page>/?akce=prihlaseni&amp;chyba=ano</form-error-page>
    </form-login-config>
</login-config>

A přidáme HTML stránku, obsahující formulář. V našem případě je začleňená do JSP skriptů, ale může to být úplně obyčejná (X)HTML stránka, která obsahuje formulář se správně pojmenovanými políčky: j_username a j_password.

<form method="post" action="j_security_check">
    <fieldset>
    <label>Jméno: <input type="text" name="j_username"/></label><br/>
    <label>Heslo: <input type="password" name="j_password"/></label><br/>
    <button value="submit">Přihlásit se</button>
    </fieldset>
</form>

Všimněte si cíle, kam se formulář odesílá: action="j_security_check". Žádný skript s tímto názvem ale nepíšeme – odeslaný formulář si odchytí server a postará se o ověření uživatele.

Pomocí <form-error-page> nastavíme stránku, která se má zobrazit, pokud uživatel zadá špatné heslo. Opět se může jednat o prostou HTML stránku. V našem případě je to JSP, které uživateli znovu zobrazí přihlašovací formulář, ale doplní k němu hlášku, že jeho předchozí pokus o přihlášení nevyšel.

Mezi metodami autentizace můžeme snadno přepínat podle toho, jak se nám to pro danou aplikaci hodí nebo co požaduje zákazník. HTTP autentizaci oceníte hlavně v případě, že budete psát nějaké API a s vaší aplikací nebude komunikovat člověk, ale nějaký jiný program. Naopak pro většinu uživatelských aplikací asi sáhnete po formulářové autentizaci (což vám ale nebrání aplikaci začít vyvíjet s HTTP BASIC a HTML formuláře dodělat až časem).

Kromě těchto dvou metod se může uživatel prokazovat i klientským certifikátem, který se ověřuje vůči certifikační autoritě nastavené v příslušné doméně.

Odhlašování

Při HTTP BASIC autentizaci je poněkud problematické odhlašování uživatele (spolehlivě funguje snad jen zavření prohlížeče). Formulářová autentizace nám oproti tomu nabízí snadnější odhlašování. Stačí v JSP stránce zneplatnit aktuální sezení:

<jsp:scriptlet>session.invalidate();</jsp:scriptlet>

Nebo můžete použít čistější řešení – ukončíte sezení pomocí servletu a následně přesměrujete na stránku, která uživateli řekne, že byl odhlášen.

Deklarativní bezpečnost

Zatím jsme se pořád pohybovali v prezentační-webové vrstvě, dokázali jsme uživateli zabránit v přístupu k určité části našeho webu ( /sprava/*), ale to je trochu málo. Co když programátor nebo kodér JSP stránek zapomene na nějaký if? Co když k nově přidané stránce nepřidá patřičné kontroly? Na bezpečnost bychom měli dbát hlavně v nižších vrstvách aplikace a nespoléhat se jen na to, že jsme uživateli ten formulář nebo skript znepřístupnili.

Tím se dostáváme k jedné z nejsilnějších zbraní Javy v této oblasti – k deklarativní bezpečnosti. Uživatel, kterého jsme ověřili na webu, získal určité role a ty se nesou s jeho požadavky i do nižších vrstev aplikace – když voláme metody obchodní logiky.

V naší aplikaci máme např. EJB, které umožňuje zakládání nových podniků – obsahuje tuto metodu:

public void zalozPodnik(Podnik p) {
    podnikDAO.uloz(p);
}

Prostým doplněním anotace ji ochráníme před neoprávněným přístupem:

@RolesAllowed("opravneny")
public void zalozPodnik(Podnik p) {
    podnikDAO.uloz(p);
}

Tím zajistíme, že ji může volat pouze přihlášený uživatel, který disponuje rolí opravneny. Nemusíme psát žádné if (uzivatel.role == "…") { … } else { … }. Prostor pro možné chyby se tak výrazně zmenší. I kdyby selhaly všechny ochrany a kontroly v prezentační vrstvě , k neautorizovanému volání metody nedojde – vyústí totiž ve vyvolání výjimky EJBAccessException.

Díky deklarativnímu zabezpečení můžete psát skutečně spolehlivé aplikace, které obstojí třeba i v bankovním prostředí. Ovšem nevykládejte si to špatně: zkazit se totiž dá cokoli a deklarativní přístup proto chápejte jako velmi dobrý předpoklad k vysoké bezpečnosti – nikoli jako podmínku dostačující.

Naše aplikace Nekuřák.net

Na adrese nekurak.net najdete aktuální verzi aplikace. Můžete si vyzkoušet přihlašování a odhlašování. Jméno je: zdrojak.root.cz a heslo: heslo. Výpis podniků je přístupný všem. Přidávat nové podniky může jen přihlášený uživatel.

Přidávejte záznamy do databáze dle libosti (ale počítejte s tím, že je budu občas promazávat – aplikace ještě není v normálním provozu). Schválně si vyzkoušejte přidat podnik, když nejste přihlášeni – formulář je sice normálně přístupný (není totiž v /sprava/*), ale přidání záznamu se vám nepodaří právě díky @RolesAllowed("opravneny").

Validace v prezentační vrstvě zatím žádná není – pokud se tedy pokusíte např. zadat jako číslo popisné písmenka, dostanete obecnou chybu (500) bez dalšího vysvětlení. Tato chyba je zachycena už na úrovni JSP (požadavek nedojde k databázi), jelikož proměnná cisloPopisne v třídě Podnik je typu int.

Závěr

Dnes jsme se naučili ověřovat uživatele pomocí HTTP BASIC i formulářové autentizace a ukázali jsme si výhody, které skýtá zabezpečení deklarované pomocí anotací. Přístě se naučíme ověřovat uživatele vůči databázi a LDAPu. A taky si řekneme něco málo k EJB, ke kterým jsme se zatím nedostali.

Odkazy

Komentáře

Subscribe
Upozornit na
guest
10 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
První

„třeba uděláte alternativní verzi pro wap a… bavíme se tím dodnes :-)“

+1 :-D

uf

Od vikendu to projedu postupne cely serial

piskot

Dekuji za clanek – serial.
Zajimalo by me, jak zabezpecit heslo – aby se posilalo sifrovane (hash). Bude nekdy rec i o https?

Palo

1) To su zname principy ktore nesuvisia nijako s Javou maximalne Javascript.
2) Ak chcete mat prihlasovaci formular tak proste uzivatela presmerujte na prihlasovaciu stranku ktora je v HTTPs a tam daje obycajny submitovaci formular. Heslo vam pride cez kryptovany kanal takze sa neda odsledovat cestou po sieti ale server ho vidi cleartext. Hned pri vstupe do servra ho HASHujte (sha256). A potom mozete porovnavat a ukladat napriklad do databazy.
3) Cela problematika je radovo vacsia a komplikovanejsia. Mozete pouzivat rozne druhy prihlasovania (Basic, Form, Token, …). Mozete pouzivat kryptovanie cez HTTPS ale iba po predradeny server ktory koli vykonu zabezpecuje kryptovanie a dalej je uz nekryptovane. Mozete heslo porovnavat voci DB, LDAP-u inemu centralnemu ulozisku. Uxistuju frameworky ktore vam mozu pomoct. Jednak standardny JAAS alebo napriklad spring-security (acegi). Bezpecnost v standardizovanom WEB containeri. No a k tomu este mate specifika pre rozne komerne a free aplikacne servre.

Takze si asi budete musiet nastudovat nejaky poriadne dlhy manual.

uf

Ale vyresene to po tech letech v Jave asi je, ne?

b*d

Když použijete spring-security, tak si můžete kanál vynutit. Například jenom pro přihlášení nebo „přihlášený“ pouze https atp. Pak další bezpečnostní prvky jako je kopírování session, tj. zneplatnění té, kde jste byl anonimní je otázka jednoho řádku.

v6ak

Bude řeč i o Cross-site Request Forgery?

MeDon

Nechybi tam @DeclareRoles? K cemu @DeclareRoles vubec je? :-)

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.