„Szoftverfejlesztés J2EE platformon - Security” változatai közötti eltérés
Új oldal, tartalma: „{{GlobalTemplate|Valaszthato|J2EESecurity}} Gondolom, elég sokan vagyunk, akik úgy képzelünk el egy webes bejelentkezést, hogy a felhasználónak ki kell tölten…” |
aNincs szerkesztési összefoglaló |
||
(Egy közbenső módosítás, amit egy másik szerkesztő végzett, nincs mutatva) | |||
1. sor: | 1. sor: | ||
Gondolom, elég sokan vagyunk, akik úgy képzelünk el egy webes bejelentkezést, hogy a felhasználónak ki kell töltenie valamilyen űrlapot a nevével és a jelszavával, a háttérben erre a program kiszedi a megfelelő sort az adatbázisból, és ennek alapján eldönti, hogy jó-e a jelszó, avagy rossz, admin-e a felhasználó vagy sem, stb. Ez a folyamat az autentikáció. Ezután a felhasználó meg akar nézni valamilyen lapot a biztonságos területről. A rendszernek döntenie kell, hogy beengedje-e, avagy sem. Ez az autorizáció. Aki csinált már valamilyen webes bejelentkező felülettel rendelkező cuccot PHP-ban, az megszokta, hogy mindezt saját magának kell leprogramoznia. Erre természetesen Java EE-ben is van lehetőség (a PHP-hoz képest kisebb csinosításokkal, úgymint filterek beiktatása, entity beanek használata sima query-k helyett, stb.), szükség azonban nincs rá. Tudvalevő, hogy a Java EE biztonsági szolgáltatása a programozó kezébe ad egy JAAS-alapú (Java Authentication & Authorization Service) módszert az autentikáció és az autorizáció (a továbbiakban AA) kezelésére. Hogy miért jó ez? | |||
Gondolom, elég sokan vagyunk, akik úgy képzelünk el egy webes | |||
bejelentkezést, hogy a felhasználónak ki kell töltenie valamilyen űrlapot a | |||
nevével és a jelszavával, a háttérben erre a program kiszedi a megfelelő | |||
sort az adatbázisból, és ennek alapján eldönti, hogy jó-e a jelszó, avagy | |||
rossz, admin-e a felhasználó vagy sem, stb. Ez a folyamat az autentikáció. | |||
Ezután a felhasználó meg akar nézni valamilyen lapot a biztonságos | |||
területről. A rendszernek döntenie kell, hogy beengedje-e, avagy sem. Ez az | |||
autorizáció. Aki csinált már valamilyen webes bejelentkező felülettel | |||
rendelkező cuccot PHP-ban, az megszokta, hogy mindezt saját magának kell | |||
leprogramoznia. Erre természetesen Java EE-ben is van lehetőség (a PHP-hoz | |||
képest kisebb csinosításokkal, úgymint filterek beiktatása, entity beanek | |||
használata sima query-k helyett, stb.), szükség azonban nincs rá. Tudvalevő, | |||
hogy a Java EE biztonsági szolgáltatása a programozó kezébe ad egy | |||
JAAS-alapú (Java Authentication & Authorization Service) módszert az | |||
autentikáció és az autorizáció (a továbbiakban AA) kezelésére. Hogy miért jó ez? | |||
* Valakik már megírták helyetted a kódot. Valószínű, hogy az évek során sikerült egy csomó rést kiküszöbölni (lásd pl. SQL-injektálhatóság), úgyhogy feltehetően jobban jársz egy ilyennel. | * Valakik már megírták helyetted a kódot. Valószínű, hogy az évek során sikerült egy csomó rést kiküszöbölni (lásd pl. SQL-injektálhatóság), úgyhogy feltehetően jobban jársz egy ilyennel. | ||
* Ha deklaratívan kezeled az AA-t, akkor a jogosultságok nincsenek bedrótozva a programba: egy-két telepítésleíró-bejegyzés beiktatásával vagy elvételével bővíthető vagy kurtítható egy-egy szerepkör felhasználói tábora. További hatalmas előny, hogy az AA adatokat tartalmazó erőforrás jellegétől (Egy fájl? Egy adatbázis? Egy...?) is független maradhatsz. | * Ha deklaratívan kezeled az AA-t, akkor a jogosultságok nincsenek bedrótozva a programba: egy-két telepítésleíró-bejegyzés beiktatásával vagy elvételével bővíthető vagy kurtítható egy-egy szerepkör felhasználói tábora. További hatalmas előny, hogy az AA adatokat tartalmazó erőforrás jellegétől (Egy fájl? Egy adatbázis? Egy...?) is független maradhatsz. | ||
A Java EE biztonsági szolgáltatásában ún. realmek (ejtsd "relm", nem pedig | A Java EE biztonsági szolgáltatásában ún. realmek (ejtsd "relm", nem pedig "rílm") használatosak egy alkalmazás felhasználói táborának azonosítására. Autentikálják a felhasználót, aki egy biztonsági kontextust kap, benne a felhasználói azonosítójával és a csoportazonosítójával (esetleg több ilyenis lehet bizonyos realmeknél). Az autorizáció ezután a konténer feladata, a telepítésleírók alapján. | ||
"rílm") használatosak egy alkalmazás felhasználói táborának azonosítására. | |||
Autentikálják a felhasználót, aki egy biztonsági kontextust kap, benne a | |||
felhasználói azonosítójával és a csoportazonosítójával (esetleg több | |||
telepítésleírók alapján. | |||
A továbbiak kizárólag Netbeans 5.5-re, és a hozzácsomagolt AS-re | A továbbiak kizárólag Netbeans 5.5-re, és a hozzácsomagolt AS-re |
A lap jelenlegi, 2014. augusztus 21., 21:23-kori változata
Gondolom, elég sokan vagyunk, akik úgy képzelünk el egy webes bejelentkezést, hogy a felhasználónak ki kell töltenie valamilyen űrlapot a nevével és a jelszavával, a háttérben erre a program kiszedi a megfelelő sort az adatbázisból, és ennek alapján eldönti, hogy jó-e a jelszó, avagy rossz, admin-e a felhasználó vagy sem, stb. Ez a folyamat az autentikáció. Ezután a felhasználó meg akar nézni valamilyen lapot a biztonságos területről. A rendszernek döntenie kell, hogy beengedje-e, avagy sem. Ez az autorizáció. Aki csinált már valamilyen webes bejelentkező felülettel rendelkező cuccot PHP-ban, az megszokta, hogy mindezt saját magának kell leprogramoznia. Erre természetesen Java EE-ben is van lehetőség (a PHP-hoz képest kisebb csinosításokkal, úgymint filterek beiktatása, entity beanek használata sima query-k helyett, stb.), szükség azonban nincs rá. Tudvalevő, hogy a Java EE biztonsági szolgáltatása a programozó kezébe ad egy JAAS-alapú (Java Authentication & Authorization Service) módszert az autentikáció és az autorizáció (a továbbiakban AA) kezelésére. Hogy miért jó ez?
- Valakik már megírták helyetted a kódot. Valószínű, hogy az évek során sikerült egy csomó rést kiküszöbölni (lásd pl. SQL-injektálhatóság), úgyhogy feltehetően jobban jársz egy ilyennel.
- Ha deklaratívan kezeled az AA-t, akkor a jogosultságok nincsenek bedrótozva a programba: egy-két telepítésleíró-bejegyzés beiktatásával vagy elvételével bővíthető vagy kurtítható egy-egy szerepkör felhasználói tábora. További hatalmas előny, hogy az AA adatokat tartalmazó erőforrás jellegétől (Egy fájl? Egy adatbázis? Egy...?) is független maradhatsz.
A Java EE biztonsági szolgáltatásában ún. realmek (ejtsd "relm", nem pedig "rílm") használatosak egy alkalmazás felhasználói táborának azonosítására. Autentikálják a felhasználót, aki egy biztonsági kontextust kap, benne a felhasználói azonosítójával és a csoportazonosítójával (esetleg több ilyenis lehet bizonyos realmeknél). Az autorizáció ezután a konténer feladata, a telepítésleírók alapján.
A továbbiak kizárólag Netbeans 5.5-re, és a hozzácsomagolt AS-re vonatkoznak. (Ezt használom, ezt ismerem...) Az életszerűség kedvéért hagyom a fenébe a konfigfájlalapú (file realm) azonosítást, helyette az adatbázis-alapú azonosítást (JDBCRealm) fogom bemutatni.
Mik egy biztonságos program fejlesztésének lépései, ha Java EE Securityben gondolkozunk?
- Adatbázistáblák létrehozása a felhasználónév, a jelszó és a csoportazonosító tárolására
- Adatok beírása az adatbázisba ;)
- Alkalmazásszerver elindítása, Admin console megnyitása, bejelentkezés (alapértelmezés: admin/adminadmin)
- Ámulás-bámulás: a Configuration -> Security (a baloldali menüben a kis nyílra kattints!) -> Realms alatt csak olyasmik látszanak, mint a file realm (bármilyen komolyabb célra használhatatlan) és a certificate realm (ennek a valós életben lehet értelme, egy házi feladatban már kevésbé). Hozzá kell adni a listához a JDBCRealmet. A New... gombot megnyomva egy űrlapot kapunk. Az adatok:
- Realm: JDBCRealm
- Class name: com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm
- Hozzá kell továbbá adni egy vagon propertyt az Add Property gombbal. Ezek a következők:
- datasource-jndi (az adatbázis kapcsolat JNDI neve)
- user-table (a felhasználói neveket és jelszavakat tároló tábla)
- user-name-column (a felhasználói név oszlopa)
- password-column (a jelszó oszlopa)
- group-table (a csoport táblája, mely akár azonos is lehet a user-table-ben megadottal)
- group-name-column (a csoportnév oszlopa)
- jaas-context (kötelezően jdbcRealm, pontosan ilyen betűnagyságokkal)
- digest-algorithm (a jelszóhoz tartozó digest előállításának algoritmusa - ha ilyet nem akarsz, ez a property none legyen).
- Ok.
- A group-table tábla group-name-column mezejéből kivett csoportnevet össze kell rendelni a konténer által azonosítható biztonsági szerepkörökkel (role). Ennek módja a telepítésleíró (sun-application.xml) megbuherálása. Először is meg kell adni neki a fasza kis GUI-n, hogy a JDBCRealmet használja, majd Edit as XML. Be kell szúrni a kívánt mennyiségű összerendelést a következőképp:
<security-role-mapping> <role-name>client</role-name> <group-name>client</group-name> </security-role-mapping>
Asszem nemigen szükséges magyarázat, hogy hova mit írj (a csoport és a szerepkör neve lehet azonos, de ez korántsem kötelező, valamint egy szerepkörhöz több csoport is lehet rendelve).
- Pl. web réteges AA beállításához security constrainteket kell beállítani a telepítésleíróban (EJB rétegben is lehet használni deklaratív jellegű AA-t, de ott annotációk szükségesek. Ennek sajnos nem néztem utána, majd valaki más... :) ). Nyisd ki a web.xml-t, és menj a Security fülre! Az autentikáció típusa legyen Form, a realm neve JDBCRealm, a Form Login Page és a Form Error Page értelemszerűen kitöltendő egy-egy magunk készítette lap címével. Most a Security Roles rész jön. Ide kell beszúrni az alkalmazásban használni kívánt biztonsági szerekörök nevét. (Nem árt, ha a csoport-szerepkör összerendelésnél mindkét fél létezik...) Végül a Security Constraints részben definiálni kell a jogosultsági korlátokat a megfelelő URL mintákra. Fontos, hogy az Enable Authentication Constraint be legyen ikszelve, és hozzá legyen adva az Edit gombbal az engedélyezni kívánt szerepkör. Az sem árt továbbá, ha a Form Login Page nem a védett területen belül van. A bejelentkeztetést végző űrlap szerkezete kötött:
- a form tag action paramétere mindenképp j_security_check
- a felhasználónév egy text típusú, j_username nevű szövegmező
- a jelszó egy password típusú, j_password nevű szövegmező
No, nagyjából ennyi: ha ezt mind végigcsináltad, kaptál egy működő AA-t a web rétegben, egyetlen sor Java kód beírása nélkül. Ugye, hogy nem rossz? :)
Amit még érdemes tudni:
- A form HTML-tagnek ne legyen ilyen attribútuma: enctype="multipart/form-data".
- A login form csak akkor fog feljönni, ha authentikációhoz kötött oldalt szeretnél elérni. Direkt meghívni nem szerencsés (nem fog működni). Ha jól adod meg a felhasználónevet és a jelszót, akkor az elküld gomb után megkapod a kért oldalt.
Források:
- Imre Gábor diái :)
- http://blogs.sun.com/swchan/entry/jdbcrealm_in_glassfish
- http://www.developinjava.com/readarticle.php?article_id=5
Programmatic Login
Ha minden oldalra szeretnél kirakni egy egyedi login form-ot, akkor a _ProgrammaticLogin_-ra lesz szükséged. A ProgrammaticLogin osztály használatához szükség lesz a Glassfish lib könyvtárában lévő lévő appserver-rt.jar fájlra. NetBeansben: a pojectben a Libraries mappára jobb klikk, Add JAR/Folder...
Ezzel megoldható az is, hogy egy JSF-es űrlaphoz tartozó backing bean kezelje a bejelentkezést, illetve a kijelentkezést is. Hátránya, hogy appszerverfüggő. Íme egy Glassfish-es megoldás vázlata:
import com.sun.appserv.security.ProgrammaticLogin; ... ProgrammaticLogin programmaticLogin; programmaticLogin = new ProgrammaticLogin(); .... programmaticLogin.login(username, password, "JDBCRealm", getRequest(), getResponse(), true); ... programmaticLogin.logout(getRequest(), getResponse());
A HttpServletRequest és HttpServletResponse objektumokat így tudod megszerezni backing beanből:
FacesContext ctx = FacesContext.getCurrentInstance(); ExternalContext ectx = ctx.getExternalContext(); this.req = (HttpServletRequest) ectx.getRequest(); this.resp = (HttpServletResponse) ectx.getResponse();
Ezekre azért van szükség, mert sikeres authentikáció után ide kerül be a session azonosító. Persze ha csak meghívsz egy metódust a jogosultságokkal, és rögtön ki is lépsz (ugyanabban a backing bean metódusban), akkor elég lehet a HttpServlet* paraméterek nélkül login() és logout() metódus is (bár ezt nem próbáltam).
Egy példa login form:
<f:view> <h:form> <table border="1" cellpadding="1"> <tr> <th colspan="2">Login form</th> </tr> <tr> <th><h:outputLabel for="UsernameField" value="Username:" /></th> <td> <h:inputText id="UsernameField" value="#{LoginManagedBean.username}" required="true" /> </td> <td><h:message for="UsernameField" /></td> </tr> <tr> <th><h:outputLabel for="PasswordField" value="Password:" /></th> <td> <h:inputText id="PasswordField" value="#{LoginManagedBean.password}" required="true" /> </td> <td><h:message for="PasswordField" /></td> </tr> <tr> <td> <h:commandButton type="submit" value="Login" action="#{LoginManagedBean.login}" /> </td> <td colspan="2"> <h:messages globalOnly="true" /> </td> </tr> </table> </h:form> </f:view>
EJB-security
Ha JAAS-el authentikálod magad, akkor azt a webréteg továbbterjeszti az EJB-réter felé is. Session bean-ben így tudod lekérdezni a bejelentkezett felhasználó nevét:
@Resource SessionContext sessionContext; ... String username = sessionContext.getCallerPrincipal().getName();