Autentizace pomocí filtrů – správce uživatelů
V tomto článku vytvoříme třídu, jejíž instance bude mít na starosti přihlášení a zaregistrování uživatele. Informace o uživatelích jsou stejně jako při použití JDBCRealm uloženy v relační databázi. Všechny potřebné informace (názvy tabulek, názvy sloupců a podobně) budou nastaveny v konfiguračním souboru web.xml pomocí elementu context-param.
Třída UserManager
Každý realm se nastavuje tagem Realm
v konfiguračním souboru v adresáři conf, kde jsou rozhodující hodnoty atributů. Jedním z atributů je className
, který určuje, o jaký realm se jedná. Další atributy jsou závislé na typu realmu. Seznam atributů JDBCRealmu i s popisem lze nalézt v dokumentaci TomCatu nebo v článku Java Servlets – autorizovaný prístup k aplikácii IV.
Naše řešení musí mít stejné možnosti konfigurace jako JDBCRealm a musí být konfigurovatelné z konfiguračních souborů. Vytvoříme třídu, která bude uchovávat všechny potřebné informace, které jsou uloženy v atributech elementu Realm
. Vynecháme pouze className
(ten je pro nás zbytečný) a atribut debug
(logovat nic nebudeme, nebyl by ale problém logování připsat). Pro všechny ostatní atributy elementu Realm
vytvoříme atributy naší třídy, kterou pojmenujeme UserManager
.
Instanci naší třídy UserManager
nebudeme konfigurovat v XML souboru v adresáři conf pomocí elementu Realm
, ale v souboru web.xml pomocí elementu context-param
.
Atributy třídy UserManager
(zdrojový kód):
/** Spojení s databází */
private java.sql.Connection connection;
/** Přihlašovací jméno do DB. */
private java.lang.String connectionName;
/** Přihlašovací heslo do DB */
private java.lang.String connectionPassword;
/** URL pro připojení k DB.*/
private java.lang.String connectionURL;
/** Šifrovací algoritmus pro ukládání hesel */
private java.lang.String digest;
/** Jméno JDBC ovladače pro práci s databází. */
private java.lang.String driverName;
/** Název sloupce se jmény rolí */
private java.lang.String roleNameCol;
/** Jméno sloupce s přístupovými hesly. */
private java.lang.String userCredCol;
/** Jméno sloupce s jmény uživatelů. */
private java.lang.String userNameCol;
/** Jméno tabulky uživatelů.*/
private java.lang.String userTable;
/** Jméno tabulky rolí. */
private java.lang.String userRoleTable;
Navíc je zde atribut connection
, který reprezentuje připojení k databázi. Pro všechny atributy jsou k dispozici veřejné metody set
a get
.
Dále bude třída obsahovat metody login
a register
. Spojení s databází otevřeme metodou openConnection
:
public void openConnection() throws java.sql.SQLException, ClassNotFoundException
{
closeConnection();
Class.forName(getDriverName());
setConnection(java.sql.DriverManager.getConnection(getConnectionURL(), getConnectionName(), getConnectionPassword()));
}
V metodách login
a register
musíme zajistit zpracování uživatelského hesla vhodným (případně žádným) šifrovacím algoritmem podle nastavení elementu digest
. Použijeme (opíšeme) jednoduchou metodu Digest
ze zdrojových textů TomCatu, konkrétně ze souboru RealmBase.java. Metoda má dva parametry. Prvním je heslo a druhým je šifrovací algoritmus.
Metoda login
Metoda login bude zavolána v momentě, kdy uživatel vyplní a odešle přihlašovací jméno a heslo na přihlašovacím formuláři. V případě, že metoda vrátí null
, uživatel není správně autentizován. V opačném případě vrátí instanci třídy MyPrincipal
, která bude obsahovat kontejner názvu rolí, se kterými je uživatel asociován.
public MyPrincipal login(String loginName, String loginPassword) throws java.sql.SQLException
{
java.sql.Statement statement = getConnection().createStatement();
String userTable = getUserTable();
String userRolesTable = getUserRoleTable();
String userNameCol = getUserNameCol();
String passwordCol = getUserCredCol();
String roleNameCol = getRoleNameCol();
String password = loginPassword;
String digest = getDigest();
if ((digest != null) && (!““.equals(digest)))
{
password = Digest(password, digest);
}
String query = „SELECT “ + roleNameCol + „, “ + userTable +
„.“ + userNameCol + “ FROM “ + userTable +
„, “ + userRolesTable + “ WHERE “ + userTable + „.“ +
userNameCol + “ = “ + userRolesTable + „.“ +
userNameCol + “ AND “ +
userTable + „.“ + userNameCol + “ = ‚“ +
transform(loginName) + „‚ AND “ +
passwordCol + “ = ‚“ + transform(password) + „‚“;
java.sql.ResultSet rs = statement.executeQuery(query);
MyPrincipal principal = null;
while (rs.next())
{
if (principal == null)
{
principal = new MyPrincipal();
principal.setName(rs.getString(userTable + „.“ + userNameCol));
}
principal.addRole(rs.getString(roleNameCol));
}
rs.close();
statement.close();
return principal;
}
SQL dotaz sestavíme s hodnot atributů, které máme z web.xml.
Metoda register
Metoda bude zavolána při registraci uživatele. Metoda zapíše uživatele do tabulky uživatelů a role do tabulky rolí. Pomocí transakce zajistíme, aby byl uživatel zaregistrován kompletně nebo vůbec. V případě, že metoda nevyvrhne výjimku, proběhla registrace úspěšně. Je vhodné mít v tabulce uživatelů (nikoli v tabulce rolí!) zajištěno databázovým systémem, aby přihlašovací jméno bylo jednoznačné. Kdyby tomu tak nebylo, je nutné ještě zkontrolovat, jestli již dané přihlašovací jméno neexistuje.
Metoda register
je napsána již s ohledem na ukázkovou aplikaci, které se budu věnovat v posledním článku. V prvním článku jsem sliboval, že při použití autentizačního mechanizmu popsaného v tomto seriálu nebude nutné měnit zdrojový text WWW aplikace, pro kterou autentizaci vytváříme. Znamená to, že registrace je již v aplikaci vyřešená. V tom případě se může i nadále používat programový kód pro registraci, která je již hotová, místo metody register
.
public void register(String userName, String userPassword, java.util.Vector roles) throws java.sql.SQLException
{
boolean autoCommit;
java.sql.Connection con = getConnection();
autoCommit = con.getAutoCommit();
userName = transform(userName);
userPassword = transform(userPassword);
con.setAutoCommit(false);
String userTable = getUserTable();
String userRolesTable = getUserRoleTable();
String userNameCol = getUserNameCol();
String passwordCol = getUserCredCol();
String roleNameCol = getRoleNameCol();
String password = userPassword;
String digest = getDigest();
if ((digest != null) && (!““.equals(digest)))
{
password = Digest(password, digest);
}
String insertUserQuery = „INSERT INTO “ + userTable + „(“ +
userNameCol + „, “ + passwordCol + „) VALUES (‚“ +
userName + „‚, ‚“ + transform(password) + „‚)“;
String insertRoleQuery = „INSERT INTO “ + userRolesTable +
„(“ + roleNameCol + „, “ + userNameCol +
„) VALUES(?,'“ + userName +“‚)“;
try
{
java.sql.Statement statement = con.createStatement();
statement.executeUpdate(insertUserQuery);
statement.close();
java.sql.PreparedStatement pStatement = con.prepareStatement(insertRoleQuery);
for(java.util.Enumeration e = roles.elements(); e.hasMoreElements();)
{
pStatement.setString(1, (String)e.nextElement());
pStatement.executeUpdate();
}
con.commit();
con.setAutoCommit(autoCommit);
}
catch (java.sql.SQLException ex)
{
con.rollback();
con.setAutoCommit(autoCommit);
throw ex;
}
}
Vytvoření instance
Instance této třídy musí být dostupná v rámci celé aplikace. Bude registrována v objektu application
jako atribut aplikace. Otázka spíše je, kdy ji vytvořit a zaregistrovat. Musí to být v okamžiku, kdy je vytvářená aplikace (instance typu ServletContext
).
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Proč investovat do nejvýkonnějších VPS s AMD EPYC procesory
14. června 2024 -
Praktické rady na zabezpečení redakčního systému WordPress
27. února 2023 -
Responzivní design: Proč by ho neměl ignorovat žádný vývojář?
27. listopadu 2023
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024