# HG changeset patch # User František Kučera # Date 1328570859 -3600 # Node ID aff44e80f418a723f7075e2ef1564f012493d9e9 # Parent e013564c8e6f171d5bf074eb14055860e508debe Napojení na SQL databázi. diff -r e013564c8e6f -r aff44e80f418 analýza/jaas.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analýza/jaas.txt Tue Feb 07 00:27:39 2012 +0100 @@ -0,0 +1,26 @@ +Java – Glassfish – JAAS +----------------------- + +Důležité pojmy: + - Realm – vytváří se jen jednou, obsahuje parametry (Properties) + - Login modul – vytváří se při každém pokusu o přihlášení + + ---------------------------------- + Uživatel ← → Login modul ← → Realm + ---------------------------------- + +V konfiguraci Glassfishe definujeme ověřování uživatelů jako Realm, ke kterému zadáme JAAS Context. +Nemůžeme tam zadat cokoli, musí to být hodnota z login.conf – podle ní se totiž dohledá příslušný Login modul. + +Viz konfigurace/glassfish/login.conf + +Knihovnu sql-java-prihlasovani.jar musíme dát do /opt/glassfish3/glassfish/lib/ +ne do /opt/glassfish3/glassfish/domains/domain1/lib/ext/ +Jinak bychom totiž dostali tuto chybu: + +-------------------------------------------------------------------------------- +[#|2012-02-06T20:04:34.502+0100|SEVERE|glassfish3.1.1|javax.enterprise.system.tools.admin.com.sun.enterprise.v3.admin|_ThreadID=109;_ThreadName=Thread-2;|Exception in command execu +tion : java.lang.NoClassDefFoundError: com/sun/appserv/security/AppservRealm +java.lang.NoClassDefFoundError: com/sun/appserv/security/AppservRealm +… +-------------------------------------------------------------------------------- diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/nbproject/build-impl.xml --- a/java/sql-java-prihlasovani/nbproject/build-impl.xml Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/nbproject/build-impl.xml Tue Feb 07 00:27:39 2012 +0100 @@ -20,10 +20,10 @@ --> - + - + @@ -198,6 +198,7 @@ + @@ -384,6 +385,7 @@ + @@ -514,7 +516,7 @@ - + @@ -839,16 +841,26 @@ --> + + + + + + + + - + + + diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/nbproject/genfiles.properties --- a/java/sql-java-prihlasovani/nbproject/genfiles.properties Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/nbproject/genfiles.properties Tue Feb 07 00:27:39 2012 +0100 @@ -4,5 +4,5 @@ # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=916fc21a -nbproject/build-impl.xml.script.CRC32=7e41f0b2 -nbproject/build-impl.xml.stylesheet.CRC32=0c01fd8e@1.43.1.45 +nbproject/build-impl.xml.script.CRC32=2d24efe9 +nbproject/build-impl.xml.stylesheet.CRC32=fcddb364@1.50.1.46 diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/nbproject/project.properties --- a/java/sql-java-prihlasovani/nbproject/project.properties Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/nbproject/project.properties Tue Feb 07 00:27:39 2012 +0100 @@ -1,9 +1,10 @@ annotation.processing.enabled=true annotation.processing.enabled.in.editor=false -annotation.processing.processor.options= annotation.processing.processors.list= annotation.processing.run.all.processors=true annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=sql-java-prihlasovani +application.vendor=fiki build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: @@ -24,6 +25,7 @@ dist.dir=dist dist.jar=${dist.dir}/sql-java-prihlasovani.jar dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= excludes= includes=** jar.compress=false @@ -34,8 +36,8 @@ javac.deprecation=false javac.processorpath=\ ${javac.classpath} -javac.source=1.6 -javac.target=1.6 +javac.source=1.7 +javac.target=1.7 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir}:\ diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLLoginModul.java --- a/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLLoginModul.java Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLLoginModul.java Tue Feb 07 00:27:39 2012 +0100 @@ -1,33 +1,58 @@ package cz.frantovo.jaas.sql; import com.sun.appserv.security.AppservPasswordLoginModule; +import com.sun.enterprise.security.auth.realm.NoSuchUserException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.auth.login.LoginException; /** * Přihlašovací modul pro SQL doménu. - * TODO: později bude potomkem com.sun.appserv.security.AbstractLoginModule + * + * TODO: později bude potomkem + * com.sun.appserv.security.AbstractLoginModule + * * @author fiki */ public class SQLLoginModul extends AppservPasswordLoginModule { - /** viz konfigurace v login.conf */ + /** + * viz konfigurace v login.conf + */ public static final String VÝCHOZÍ_JAAS_KONTEXT = "sqlRealm"; private static final Logger log = Logger.getLogger(SQLLoginModul.class.getName()); @Override protected void authenticateUser() throws LoginException { - - if (_currentRealm instanceof SQLRealm) { - - SQLRealm sqlRealm = (SQLRealm)_currentRealm; - String skupiny[] = sqlRealm.ověřUživatele(_username, _passwd); - commitUserAuthentication(skupiny); - + if (_currentRealm instanceof SQLRealm) { + try { + SQLRealm sqlRealm = (SQLRealm) _currentRealm; + sqlRealm.ověřUživatele(_username, _passwd); + + String skupiny[] = poleSkupin(sqlRealm.getGroupNames(_username)); + log.log(Level.INFO, "Uživatel {0} byl úspěšně ověřen a je členem těchto skupin: {1}", new Object[]{_username, Arrays.toString(skupiny)}); + commitUserAuthentication(skupiny); + } catch (NoSuchUserException nue) { + log.log(Level.SEVERE, "Uživatele se podařilo ověřit, ale při pokusu o zjištění jeho skupin došlo k chybě. Uživatel: " + _username, nue); + throw new LoginException("Neexistující uživatel: " + _username); + } } else { throw new LoginException("Špatný realm: " + _currentRealm + " Očekávám: SQLRealm."); } } + + /** + * Převede výčet na pole + */ + private static String[] poleSkupin(Enumeration e) { + List l = new ArrayList<>(); + while (e.hasMoreElements()) { + l.add(e.nextElement()); + } + return l.toArray(new String[0]); + } } diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java --- a/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Tue Feb 07 00:27:39 2012 +0100 @@ -4,22 +4,46 @@ import com.sun.enterprise.security.auth.realm.BadRealmException; import com.sun.enterprise.security.auth.realm.NoSuchRealmException; import com.sun.enterprise.security.auth.realm.NoSuchUserException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; import java.util.Properties; import java.util.logging.Level; -import java.util.logging.Logger; +import javax.naming.InitialContext; +import javax.naming.NamingException; import javax.security.auth.login.LoginException; +import javax.sql.DataSource; /** - * Bezpečnostní doména. - * Uživatelé jsou uloženi v SQL databázi. + * Bezpečnostní doména. Uživatelé jsou uloženi v SQL databázi. + * * @author fiki */ public class SQLRealm extends AppservRealm { private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi."; - private static final Logger log = Logger.getLogger(SQLRealm.class.getName()); + private static final String PARAM_JNDI = "jndi"; + private static final String PARAM_SQL_HESLO = "sql_heslo"; + private static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele"; + private static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny"; + private DataSource datovýZdroj; + private String sqlHeslo; + private String sqlSkupinyUživatele; + private String sqlSkupinyVšechny; + /** + * Načteme a zkontrolujeme parametry + * + * @param parametry + * @throws BadRealmException pokud je v parametrech chyba + * @throws NoSuchRealmException + */ @Override public void init(Properties parametry) throws BadRealmException, NoSuchRealmException { super.init(parametry); @@ -27,9 +51,26 @@ String jaasContext = parametry.getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT); setProperty(JAAS_CONTEXT_PARAM, jaasContext); - log.log(Level.INFO, "SQLRealm úspěšně vytvořen. JaasContext: {0}", jaasContext); + /** Databázové spojení */ + { + String jndi = najdiParametr(parametry, PARAM_JNDI, "název datového zdroje"); + try { + InitialContext k = new InitialContext(); + jndi = parametry.getProperty(PARAM_JNDI); + datovýZdroj = (DataSource) k.lookup(jndi); + } catch (NamingException e) { + throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndi, e); + } + } + + /** SQL dotazy */ + sqlHeslo = najdiParametr(parametry, PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla"); + sqlSkupinyUživatele = najdiParametr(parametry, PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele"); + sqlSkupinyVšechny = najdiParametr(parametry, PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin"); + + _logger.log(Level.INFO, "SQLRealm úspěšně vytvořen. JaasContext: {0}", jaasContext); } - + @Override public String getAuthType() { return AUTH_TYPE; @@ -41,24 +82,107 @@ * @throws NoSuchUserException když uživatel s tímto jménem neexistuje. */ @Override - public Enumeration getGroupNames(String uživatel) throws NoSuchUserException { - throw new NoSuchUserException("Metoda zatím není implementována. Uživatel: " + uživatel); + public Enumeration getGroupNames(String uživatel) throws NoSuchUserException { + Collection skupiny = new ArrayList<>(); + + Connection s = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + s = datovýZdroj.getConnection(); + ps = s.prepareStatement(sqlSkupinyUživatele); + ps.setString(1, uživatel); + rs = ps.executeQuery(); + + while (rs.next()) { + skupiny.add(rs.getString(1)); + } + + return Collections.enumeration(skupiny); + + } catch (Exception e) { + String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + "."; + _logger.log(Level.WARNING, hláška, e); + throw new NoSuchUserException(hláška); + } finally { + zavri(s, ps, rs); + } } /** * * @param jméno uživatelské jméno * @param heslo heslo - * @return seznam skupin, do kterých uživatel patří + * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku + * @throws LoginException pokud je jméno nebo heslo neplatné nebo došlo k jiné chybě */ - public String[] ověřUživatele(String jméno, char[] heslo) throws LoginException { + public boolean ověřUživatele(String jméno, char[] heslo) throws LoginException { - // TODO: skutečně ověřovat heslo proti databázi + Connection s = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + s = datovýZdroj.getConnection(); + ps = s.prepareStatement(sqlHeslo); + ps.setString(1, jméno); + ps.setString(2, new String(heslo)); + rs = ps.executeQuery(); - if (heslo != null && heslo.length == 3) { - return new String[]{"bezny"}; + if (rs.next()) { + String dbJméno = rs.getString(1); + if (dbJméno.equals(jméno)) { + // OK – úspěšné ověření + return true; + } else { + throw new LoginException("Nebyl nalezen správný uživatel: " + jméno + " != " + dbJméno); + } + } else { + throw new LoginException("Uživatel nebyl nalezen: " + jméno); + } + } catch (SQLException e) { + String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + "."; + _logger.log(Level.WARNING, hláška, e); + throw new LoginException(hláška); + } finally { + zavri(s, ps, rs); + } + } + + private String najdiParametr(Properties parametry, String názevParametru, String popis) throws BadRealmException { + String hodnotaParametru = parametry.getProperty(názevParametru); + + if (hodnotaParametru == null || hodnotaParametru.length() < 1) { + throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru); } else { - throw new LoginException("Heslo musí mít tři znaky :-P"); + return hodnotaParametru; + } + } + + /** + * Zavře všechno + * + * @param spojeni DB spojení + * @param prikaz DB dotaz + * @param vysledek DB výsledek + */ + private static void zavri(Connection spojeni, Statement prikaz, ResultSet vysledek) { + if (vysledek != null) { + try { + vysledek.close(); + } catch (Exception e) { + } + } + if (prikaz != null) { + try { + prikaz.close(); + } catch (Exception e) { + } + } + if (spojeni != null) { + try { + spojeni.close(); + } catch (Exception e) { + } } } } diff -r e013564c8e6f -r aff44e80f418 java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java --- a/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Thu Jul 21 23:41:05 2011 +0200 +++ b/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Tue Feb 07 00:27:39 2012 +0100 @@ -36,6 +36,7 @@ public void testGetAuthType() { String authType = realm.getAuthType(); System.out.println("authType = " + authType); - assertTrue("authType musí být nenulový a neprázdný", authType != null && authType.trim().length() > 0); + assertTrue("authType musí být nenulový", authType != null); + assertTrue("authType musí být neprázdný", authType.trim().length() > 0); } }