pdf - e-book - archive - github.com

1.17  Persistance

La persistance est la possibilité (offerte par tous les langages de programmation) de conserver en mémoire des données entre deux exécutions. Si par exemple vous utilisez des variables lors d’une exécution d’un programme, lors de sa clôture les valeurs de ces variables seront perdues. Plusieurs solutions permettent d’éviter ce problème. Une solution consiste à stocker dans un fichier les données que vous souhaitez rendre persistantes. Une autre solution consiste à utiliser une base de données.

1.17.1  Fichiers

Tout logiciel de programmation permet d’écrire des données dans un fichier. Par exemple :

package persistance;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

public class Fichier
{
 public static void main(String[] args)
 {
  BufferedReader br = null;
  String fileName = "src/persistance/Fichier.java";
  try
  {
   FileInputStream fis = new FileInputStream(fileName);
   InputStreamReader isr = new InputStreamReader(fis);
   br = new BufferedReader(isr);
   String line;
   int i = 1;
   while ((line = br.readLine()) != null)
    System.out.println(line);
  }
  catch (FileNotFoundException e)
  {
   System.out.println("Impossible d'ouvrir le fichier " + fileName
     + ".");
  }
  catch (IOException e)
  {
   System.out.println("Erreur lors de la lecture dans le fichier " + fileName
     + ".");
  }
  finally
  {
   try
   {
    if (br != null)
     br.close();
   }
   catch (IOException e)
   {
    System.out.println("Impossible de fermer le fichier " + fileName
      + ".");
   }
  }
 }
}

Télécharger le fichier

Le programme ci-dessus affiche son propre code source. On remarquera que FileInputStream est un objet permettant de lire le contenu d’un fichier (Il existe une classe File, mais elle représente un fichier dans le sens un chemin dans l’arborescence). Dans le cas où il faudrait rendre persistantes plusieurs données hétérogènes, cette méthode pourrait s’avérer fastidieuse. Il existe une méthode permettant de remédier à ce type de problème.

1.17.2  Serialization

La serialization est un mécanisme permettant de stocker des objets dans des fichiers sans se soucier du format qui sera utilisé. Observons le code suivant :

package persistance;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Wrapper implements Serializable
{
 private static final long serialVersionUID = 1L;

 private int value;

 public Wrapper(int value)
 {
  this.value = value;
 }

 public int getValue()
 {
  return value;
 }

 public void setValue(int value)
 {
  this.value = value;
 }

 @Override
 public String toString()
 {
  return "" + value;
 }

 public static Wrapper read(String fileName) throws IOException, ClassNotFoundException
 {
  ObjectInputStream ois = null;
  try
  {
   FileInputStream fis = new FileInputStream(fileName);
   ois= new ObjectInputStream(fis);
   return (Wrapper) (ois.readObject());
  }
  finally
  {
   if (ois != null)
    ois.close();
  }
 }
 
 public void write(String fileName) throws IOException
 {
  ObjectOutputStream oos = null;
  try
  {
   FileOutputStream fos = new FileOutputStream(fileName);
   oos = new ObjectOutputStream(fos);
   oos.writeObject(this);
  }
  finally
  {
   if (oos != null)
    oos.close();
  }
 }
}

public class Serialization
{
 public static void main(String[] args)
 {
  String fileName = "serialization.srz";
  Wrapper w = new Wrapper(5);
  System.out.println(w);
  try
  {
   w.write(fileName);
   w.setValue(4);
   Wrapper wBis = Wrapper.read(fileName);
   System.out.println(wBis);// 4 ou 5 ?
   
   wBis.setValue(6);
   wBis.write(fileName);
   
   
  }
  catch (IOException e) 
  {
   System.out.println("Impossible d'ouvrir le fichier " + fileName + ".");
  }
  catch (ClassNotFoundException e) 
  {
   System.out.println("Le fichier " + fileName + " est corrompu.");
  }
  
 }
}

Télécharger le fichier

Dans le cas où l’objet serialisé référence d’autres objets, ceux-ci seront aussi sérialisés. Ce procédé est donc très puissant, vous pouvez en vous y prenant bien stocker tous les objets utilisés pour un projet en quelques lignes de code.

1.17.3  JDBC

Dans le cas où un application est multi-utilisateurs, la sérialisation est malheureusement insuffisante. La base de données est un moyen de résoudre ce problème. Par exemple :

package persistance;

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBC
{
 public static void main(String[] args)
 {
  Connection c = null;
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   String url = "jdbc:mysql://localhost/test", user = "", password = "";
   c = DriverManager.getConnection(url, user, password);
   String req = "select * from test";
   Statement s = c.createStatement();
   ResultSet rs = s.executeQuery(req);
   while (rs.next())
   {
    System.out.println(rs.getInt(1) + " : " + rs.getString(2));
   }
  }
  catch (ClassNotFoundException e)
  {
   System.out.println("Pilote JDBC non installé.");
  }
  catch (SQLException e)
  {
   System.out.println(e);
  }
  finally
  {
   try
   {
    if (c != null)
     c.close();
   }
   catch (SQLException e)
   {
    System.out.println("Impossible de fermer la connection.");
   }
  }
 }
}

Télécharger le fichier

1.17.4  L’attaque par injection

Tout dialogue avec un logiciel de stockage de données peut comporter des failles de sécurité. Considérons par exemple le programme suivant :

package persistance.injection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import commandLineMenus.rendering.examples.util.*;

public class Gruyere
{
 protected Connection c = null;
 protected String login, password;

 public Gruyere()
 {
  try
  {
   Class.forName("com.mysql.jdbc.Driver");
   String url = "jdbc:mysql://localhost/test", user = "", password = "";
   c = DriverManager.getConnection(url, user, password);
  }
  catch (ClassNotFoundException e)
  {
   System.out.println("Pilote JDBC non installé.");
  }
  catch (SQLException e)
  {
   System.out.println(e);
  }
 }

 public void close()
 {
  try
  {
   if (c != null)
    c.close();
  }
  catch (SQLException e)
  {
   System.out.println("Impossible de fermer la connection.");
  }

 }

 private void saisitIdentifiants()
 {
  login = InOut.getString("login : ");
  password = InOut.getString("password : ");
 }

 public boolean connect()
 {
  saisitIdentifiants();
  boolean connexionAcceptee = false;
  try
  {
   ResultSet rs = executeConnect();
   connexionAcceptee = rs.next();
  }
  catch (SQLException e)
  {
   System.out.println(e);
  }
  if (connexionAcceptee)
   System.out.println("Connexion acceptée");
  else
   System.out.println("Accés refusé");
  return connexionAcceptee;
 }

 protected ResultSet executeConnect() throws SQLException
 {
  String req = "select * from utilisateur where login = '" + login
    + "' and password = '" + password + "'";
  Statement s = c.createStatement();
  return s.executeQuery(req);
 }

 public static void main(String[] args)
 {
  Gruyere gruyere = new Gruyere();
  gruyere.connect();
  gruyere.close();
 }
}

Télécharger le fichier

On effectue une attaque par injection en saisissant par exemple le login ’ OR 1=1#. Le soin vous est laissé de comprendre ce qu’il se passe. La requête préparée ci-dessous, se charge elle-même d’échapper les caractères spéciaux pour éviter l’injection de scripts.

package persistance.injection;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class RequetePreparee extends Gruyere
{
 protected ResultSet executeConnect() throws SQLException
 {
  String req = "select * from utilisateur where login = ? and password = ?";
  PreparedStatement s = c.prepareStatement(req);
  s.setString(1, login);
  s.setString(2, password);
  return s.executeQuery();
 }

 public static void main(String[] args)
 {
  Gruyere requetePreparee = new RequetePreparee();
  requetePreparee.connect();
  requetePreparee.close();
 }
}

Télécharger le fichier