Précédent   Forum des professionnels en informatique > Java > Serveurs, conteneurs, et Java EE > Tomcat
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 13/08/2004, 09h30   #1
Invité régulier
 
Inscription : septembre 2002
Messages : 11
Détails du profil
Informations forums :
Inscription : septembre 2002
Messages : 11
Points : 7
Points : 7
Par défaut [TOMCAT] [JAAS] Extension de LoginModule dans Tomcat

Bonjour,

je cherche désespérément une solution à l'intégration de JAAS dans Tomcat. J'ai développé ma propre classe d'authentification (LoginModule), authentification basée sur un schéma de ma base de données (JDBC).
Le test en mode console fonctionne, et j'aimerai à présent intégrer tout cela à tomcat, afin de protéger mes pages jsp et mes servlet.

Problème:
Je ne comprend pas comment le lien est effectué entre Tomcat et mon LoginModule.

J'ai effectué le paramétrage du fichier jaas.config, et l'ai installé dans le répertoire conf de tomcat.
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
GestLicJAAS {
   RdbmsLoginModule required debug="true" 
       url="jdbc:microsoft:sqlserver://SQL:1433;DatabaseName=test;User=sa;Password=admin" 
       driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"
       table="login"
       userField="login"
       passField="motdepasse"
       nameField="nom"
       fornameField="prenom"
       insertPerm="insertion"
       deletePerm="suppression"
       updatePerm="miseajour"
       selectPerm="visu";
};
Puis, configuration du fichier context.xml pour ajouter un module Realm
Code :
1
2
3
4
5
6
 
<Realm className="org.apache.catalina.realm.JAASRealm"
	appname="GestLicJAAS"
	userClassNames="com.actemium.lille.gestlic.security.RDBPrincipal"
	roleClassNames="com.actemium.lille.gestlic.security.RDBCredential"
/>
Enfin, j'ai ajouté les security constraints dans le fichier web.xml
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 
<security-constraint>
  	<display-name>WebApp Administration</display-name>
	<web-resource-collection>
		<web-resource-name>WebApp Admin</web-resource-name>
		<url-pattern>/*</url-pattern>
		<http-method>DELETE</http-method>
		<http-method>GET</http-method>
		<http-method>POST</http-method>
		<http-method>PUT</http-method>
	</web-resource-collection>
	<auth-constraint>
		<role-name>administrator</role-name>
		<role-name>other-role</role-name>
	</auth-constraint>
 
  </security-constraint>
 
  <login-config>
  	<auth-method>FORM</auth-method>
  	<realm-name>GestLicJAAS</realm-name>
  	<form-login-config>
  		<form-login-page>
  			/login.jsp
  		</form-login-page>
  		<form-error-page>
  			/error.jsp
  		</form-error-page>
  	</form-login-config>
  </login-config>
 
  <security-role>
  	<role-name>administrator</role-name>
  </security-role>
  <security-role>
	<role-name>other-role</role-name>
  </security-role>
Le déploiement fonctionne.
A la connexions sur la page, j'obtiens le message d'erreur "Impossible de trouver une configuration de connexion".

J'ai cherché au niveau des forum sans grand succés, les tutorats sur JAAS sont nombreux, mais l'intégration dans Tomcat est quelque peu trouble.

Un coup de main serait sympa.

Merci.
GiHe est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 13/08/2004, 09h36   #2
Invité régulier
 
Inscription : septembre 2002
Messages : 11
Détails du profil
Informations forums :
Inscription : septembre 2002
Messages : 11
Points : 7
Points : 7
bonjour,

un petit détail, j'ai effectué aussi la relation entre la JVM et le fichier de configuration de JAAS (jaas.config) depuis le script de lancement de Tomcat.

Code :
1
2
3
 
rem JAVA_OPTS pour TOMCAT et JASS Realms
SET JAVA_OPTS=-DJAVA_OPTS=-Djava.security.auth.login.config==%CATALINA_HOME%/conf/jaas.config
GiHe est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 13/08/2004, 15h56   #3
Invité régulier
 
Inscription : septembre 2002
Messages : 11
Détails du profil
Informations forums :
Inscription : septembre 2002
Messages : 11
Points : 7
Points : 7
ok, solution trouvée, dur dur !!!! 8)

je donne la procédure détaillée, histoire que d'autres ne mettent pas autant de temps pour mettre en place JAAS sous Tomcat.

1. Développer un module implémentant LoginModule, il faut aussi une classe Principal, et Role.

voir tutorial suivant
http://forum.java.sun.com/thread.jsp?thread=233317&forum=60&message=1216065

Soit :

Fichier RDBLoginmodule
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
 
 
package my;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
 
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
 
public class RDBLoginModule implements LoginModule {
 
    // Etat initial, nécessaire par l'implements
    CallbackHandler callbackHandler;										// Chargé de l'interface avec l'utilisateur ou tout autre moyen d'authentification
    Subject subject;														// Informations concernant l'identification (princiapl, credential, nom, ...)
    Map sharedState;														// Informations partagées entre les différents LoginModule
    Map options;															// Options complémentaires du fichier de configuration, ici, l'URL et le driver d'accès à la base de données
 
    // Etat temporaire, stockage temporaire des informations d'identification
    // Une personne peut être identifié par son nom, N°= sécu, ... == Tableau
    ArrayList tempCredentials;												// Nom ou clé publique
    ArrayList tempPrincipals;												// mot de passe ou clé privée
 
    // Statut d'authentification
    boolean success;														// true si ok !
 
    // Options de configuration récupérées dans le fichier de configuration
    String jdbcUrl;															// URL d'accès à la base de données
    String jdbcDriver;														// Pilote d'accès à la base de données
    String jdbcTable;														// Table contenant les informations de connexion
    String userField;														// Champs contenant les informations du nom/login de l'utilisateur
    String passField;														// Champs contenant le mot de passe de l'utilisateur
    String nameField;														// Nom de l'utilisateur
    String fornameField;														// Prénom de l'utilisateur
    String insertPerm;														// Champs contenant la valeur de la permission d'insérer
    String deletePerm;														// Champs contenant la valeur de la persmission de supprimer
    String updatePerm;														// Champs contenant la valeur de la permission de mettre à jour
    String selectPerm;														// Champs contenant la valeur de la permission de visualiser les données
 
    private boolean commited;
 
    public RDBLoginModule() {
        tempCredentials = new ArrayList();
        tempPrincipals = new ArrayList();
 
        success = false;
 
        jdbcUrl = null;
        jdbcDriver = null;
        jdbcTable = null;
        userField = null;
        passField = null;
        nameField = null;
        fornameField = null;
        insertPerm = null;
        deletePerm = null;
        updatePerm = null;
        selectPerm = null;
 
        callbackHandler = null;
        subject = null;
        sharedState=null;
        options=null;
        System.out.println("depuis construct RDBLoginmodule");
 
        commited = false;
    }
 
    public boolean abort() throws LoginException {
        success = false;
 
        tempPrincipals.clear();
        tempCredentials.clear();
 
        if (callbackHandler instanceof PassiveCallbackHandler) 
            ((PassiveCallbackHandler)callbackHandler).clearPassword();
 
        logout();
 
        return true;
    }
 
    public boolean commit() throws LoginException {
        if (success) {
            try {
                subject.getPrincipals().addAll(tempPrincipals);
                subject.getPrincipals().add(new RDBRolePrincipal("admin"));
            } catch (Exception e) {
                e.printStackTrace();
                throw new LoginException(e.getMessage());
            }
        }
        commited=true;
        return true;
    }
 
    public boolean login() throws LoginException {
        // Y a-t-il bien un CallbackHandler initialisé ?
        if (callbackHandler == null) {
            throw new LoginException("Erreur: pas de callback !");
        }
        try {
            // initialisation des callbacks
            Callback[] callbacks = new Callback[]{
                    // Callback pour retrouver les informations de Nom, l'argument est le prompt à afficher
                    new NameCallback("Nom:"),
                    // Callback pour le mot de passe, prompt et true pour afficher le mot de passe en le tapant
                    new PasswordCallback("Mot de passe:",false)
            };
            // Appel de la méthode handle pour récupérer les informations d'identification
            // La méthode dce récupération ne nous interesse pas ici, seul nous intéresse la récupération
            // du user et du mot de passe ou des clés
            callbackHandler.handle(callbacks);  
 
            // Récupération des informations
            // L'ordre des données est donné plus haut
            String userName = ((NameCallback)callbacks[0]).getName();
            String password = new String(((PasswordCallback)callbacks[1]).getPassword());
 
            // Clear du password dans le callbacks
            ((PasswordCallback)callbacks[1]).clearPassword();
 
            // Validation des informations 
            success = rdbValidate(userName, password);
 
            // Annulation des callbacks
            callbacks[0]=null;
            callbacks[1]=null;
 
            if (!success) {
                throw new LoginException("Authentification incorrecte !");
            }
            return success;    
        } catch (Exception ex) {
 
        }
        return false;
    }
 
    public boolean logout() throws LoginException {
        // clear des informations utilisateur et autorisations
        tempPrincipals.clear();
        tempCredentials.clear();
        if (callbackHandler instanceof PassiveCallbackHandler)
            ((PassiveCallbackHandler)callbackHandler).clearPassword();
        // Clear des informations enregistrées dans le Subject
        Iterator it = subject.getPrincipals().iterator();
        while (it.hasNext()) {
            RDBPrincipal p = (RDBPrincipal)it.next();
            subject.getPrincipals().remove(p);
        }
        it = subject.getPublicCredentials().iterator();
        while (it.hasNext()) {
            RDBCredential c = (RDBCredential)it.next();
            subject.getPublicCredentials().remove(c);
        }
        return true;
    }
 
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState,
            Map options) {
 
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.sharedState = sharedState;
        this.options = options;
        // récupération et initialisation des options
        jdbcUrl = (String) options.get("url");
        jdbcDriver = (String) options.get("driver");
        jdbcTable = (String) options.get("table");
        userField = (String) options.get("userField");
        passField = (String) options.get("passField");
        nameField = (String) options.get("nameField");
        fornameField = (String) options.get("fornameField");
        insertPerm = (String) options.get("insertPerm");
        deletePerm = (String) options.get("deletePerm");
        updatePerm = (String) options.get("updatePerm");
        selectPerm = (String) options.get("selectPerm");
        System.out.println("depuis init RDBLoginmodule");
    }
 
    private boolean rdbValidate(String userName, String password) throws LoginException {
        System.out.println("depuis validate RDBLoginmodule");
        Connection con;
        // Query avec les champs personnalisés
        String query = "SELECT * FROM " + jdbcTable + " where " + userField + "='" + userName + "'";
        Statement stmt;
        RDBPrincipal  p = null;
        RDBCredential c = null;
        boolean passwordMatch = false;
 
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException cnfex) {
            throw new LoginException("Pilote JDBC introuvable, vérifier votre CLASSPATH");
        }
        try {
            // Effectue la connexion à la base de données, requête sur l'utilisateur
            // récupération des données
            con = DriverManager.getConnection(jdbcUrl,"sa","admin");
            stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(query);
            String dbPassword = null, dbFname = null, dbLname = null;
            String updatePerm = null, deletePerm = null, insertPerm=null, selectPerm=null;
            boolean isEqual   = false;
            while (rs.next()) {
                // Test en dur !!!
                dbPassword = "jamaïc";
                dbFname = "bob";
                dbLname = "marley";
                insertPerm = "1";
                deletePerm = "1";
                updatePerm = "1";
                selectPerm = "1";
 
            }
            // Password null == utilisateur introuvable
            if (dbPassword==null) 
                throw new LoginException("Utilisateur ou mot de passe incorrect !");
            passwordMatch = password.equals(dbPassword);
 
            // Principals et Credentials
            if (passwordMatch) {
                c = new RDBCredential();
                c.setProperty("insertPerm",insertPerm);
                c.setProperty("deletePerm",deletePerm);
                c.setProperty("updatePerm",updatePerm);
                c.setProperty("selectPerm",selectPerm);
                this.tempCredentials.add(c);
                this.tempPrincipals.add(new RDBPrincipal(dbFname + " " + dbLname));
            }
 
            rs.close();
            stmt.close();
            con.close();
        } catch (SQLException sqlex) {
            sqlex.printStackTrace() ;
            System.out.println("exception capturée SQL");
            throw new LoginException(sqlex.getMessage());
        }
        return passwordMatch;
    }
    static public void main(String args[]) throws Exception{
 
        LoginContext ctx = new LoginContext("RDBLogin");
        System.out.println("depuis constructeur RDBLoginmodule");
        ctx.login();
    }
}
Fichier RDBRolePrincipal
Code :
1
2
3
4
5
6
7
8
9
 
 
package com.actemium.lille.gestlic.security;
public class RDBRolePrincipal extends RDBPrincipal {
 
    public RDBRolePrincipal(String s) {
        super(s);
    }
}
Fichier RDBPrincipal:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
 
package my;
 
import java.io.Serializable;
import java.security.Principal;
 
public class RDBPrincipal implements Principal, Serializable {
    private String name;
 
    public RDBPrincipal(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
    public boolean equals (Object another) {
        try {
            RDBPrincipal pm = (RDBPrincipal)another;
            return pm.name.equalsIgnoreCase(name);
        } catch(Exception e){
            return false;   
        }
    }
    public int hashCode() {
        return name.hashCode();
    }
    public String toString() {
        return name;
    }
}
Compiler le tout et plasser le répertoire racine du package dans le répertoire class du répertoire server de tomcat

1 bis. Créer une base de données de test et une table avec un schéma comparable à ce dernier :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
 
CREATE TABLE [dbo].[login] (
	[idlogin] [int] NOT NULL ,
	[login] [varchar] (50) COLLATE French_CI_AS NULL ,
	[motdepasse] [varchar] (50) COLLATE French_CI_AS NULL ,
	[nom] [varchar] (50) COLLATE French_CI_AS NULL ,
	[prenom] [varchar] (50) COLLATE French_CI_AS NULL ,
	[insertion] [int] NULL ,
	[suppression] [int] NULL ,
	[miseajour] [int] NULL ,
	[visu] [int] NULL 
) ON [PRIMARY]
GO
2. Pour la validation à travers une base de données, il faut utiliser une méthode de validation qui effectue la connexion à la base et requête sur la table des utilisateurs. Le mot de passe de la table des utilisateurs est comparé avec le mot de passe fournis par le CallbackHandler de Tomcat.

3. Créer un fichier de config jaas :
les variables table, url, driver, userfield, passfield, ... permettent de rendre généric la méthode de connexion quelque soit le schéma de la base utilisé.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
RDBLogin {
   my.RDBLoginModule required debug="true" 
       url="jdbc:microsoft:sqlserver://SQL:1433;DatabaseName=test;User=sa;Password=pass" 
       driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"
       table="login"
       userField="login"
       passField="motdepasse"
       nameField="nom"
       fornameField="prenom"
       insertPerm="insertion"
       deletePerm="suppression"
       updatePerm="miseajour"
       selectPerm="visu";
};
Enregistrer ce fichier dans un répertoire sous jaas.config

3. Modification du fichier server.xml :

Modifier la section Realm du fichier serv.xml

Code :
1
2
3
4
5
 
<Realm className="org.apache.catalina.realm.JAASRealm" debug="3" appName="RDBLogin" 
userClassNames="my.RDBPrincipal" 
roleClassNames="my.RDBRolePrincipal">
</Realm>
appName doit correspondre au nom donné dans le fichier jaas.config

4. Créer ou mettre à jour la variable d'environnement JAVA_OPTS:

set JAVA_OPTS=-Djava.security.auth.login.config=g:/www/jaas.config

depuis une session Dos et lancer sous la session dos la commande startup depuis le répertoire bin de Tomcat.
Cette variable est importante et permet de donner le chemin d'accès au fichier de condfiguration de JAAS.

5. Lancer un navigateur Web et aller dans la section administration de Tomcat.
Le gestionnaire de sécurité est à présent basé sur votre classe LoginModule.
Ici, la gestion se fait depuis la base de données.
Créer donc un utilisateur dans la table des utilisateurs.

ATTENTION: depuis le LoginModule, dans la méthode commit, il faut ajouter un
Code :
1
2
 
 subject.getPrincipals().add(new RDBRolePrincipal("admin"));
pour permettre à Tomcat de connaître le rôle de la personne connecté.

Voilà, j'espère que ce n'était pas trop confus, ca l'est peut-être encore un peu pour moi.

Je début sous les Servlet, JSP, Hibernate et Tomcat et met en place une application minimale de test pour mettre tout cela en oeuvre.

Ca pourrait peut-être donné lieu à un tutorial si cela interresse quelques personnes.

@+
GiHe est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/10/2005, 10h03   #4
Membre du Club
 
Inscription : avril 2003
Messages : 139
Détails du profil
Informations personnelles :
Âge : 35

Informations forums :
Inscription : avril 2003
Messages : 139
Points : 61
Points : 61
Envoyer un message via MSN à nighma Envoyer un message via Skype™ à nighma
Salut GiHe,

Je cherche exactement la même chose pour le moment afin de faire un squelette pour toutes mes applications. Je viens de passer plus d'une semaine a essayer mais sans grand succès pour le moment.

Dans la fin de ton explication tu dis que tu mets en place un application minimale de test pour mettre tout ca en oeuvre.

Serais-tu d'accord de partager tes sources ?

Si tu es d'accord peux-tu me les envoyer par mail ?

Merci
nighma est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/10/2005, 16h40   #5
Membre habitué
 
Inscription : février 2003
Messages : 136
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 136
Points : 141
Points : 141
salut nighma,
la solution de GiHe est élégante, et est un bon point de départ.
je suis parti sur la même voie et ai crée un projet open source appelé jGuard (http://jguard.sourceforge.net).

GiHe a rejoint le projet pour implémenter ensemble une solution plus aboutie.

je t'incite donc à jeter un oeil à jGuard.

cordialement,

Charles(jGuard team).
diabolo512 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 13/10/2005, 07h58   #6
Membre du Club
 
Inscription : avril 2003
Messages : 139
Détails du profil
Informations personnelles :
Âge : 35

Informations forums :
Inscription : avril 2003
Messages : 139
Points : 61
Points : 61
Envoyer un message via MSN à nighma Envoyer un message via Skype™ à nighma
Merci Charles,

Je vais de ce pas y jeter un oeil.

Je pense que c'est une bonne initiative, ça manquait.

Seb
nighma est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 06h59.


 
 
 
 
Partenaires

Hébergement Web