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.

Žádný příspěvek v diskuzi

Odpovědět