|
|
#1 (permalink) |
|
Invité régulier
![]() Date d'inscription: septembre 2002
Messages: 11
|
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 :
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"; }; Code :
<Realm className="org.apache.catalina.realm.JAASRealm" appname="GestLicJAAS" userClassNames="com.actemium.lille.gestlic.security.RDBPrincipal" roleClassNames="com.actemium.lille.gestlic.security.RDBCredential" /> Code :
<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>
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. |
|
|
|
|
|
#2 (permalink) |
|
Invité régulier
![]() Date d'inscription: septembre 2002
Messages: 11
|
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 :
rem JAVA_OPTS pour TOMCAT et JASS Realms SET JAVA_OPTS=-DJAVA_OPTS=-Djava.security.auth.login.config==%CATALINA_HOME%/conf/jaas.config |
|
|
|
|
|
#3 (permalink) |
|
Invité régulier
![]() Date d'inscription: septembre 2002
Messages: 11
|
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 :
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(); } } Code :
package com.actemium.lille.gestlic.security; public class RDBRolePrincipal extends RDBPrincipal { public RDBRolePrincipal(String s) { super(s); } } Code :
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; } } 1 bis. Créer une base de données de test et une table avec un schéma comparable à ce dernier : Code :
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 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 :
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"; }; 3. Modification du fichier server.xml : Modifier la section Realm du fichier serv.xml Code :
<Realm className="org.apache.catalina.realm.JAASRealm" debug="3" appName="RDBLogin" userClassNames="my.RDBPrincipal" roleClassNames="my.RDBRolePrincipal"> </Realm> 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 :
subject.getPrincipals().add(new RDBRolePrincipal("admin")); 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. @+ |
|
|
|
|
|
#4 (permalink) |
|
Membre régulier
![]() |
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 |
|
|
|
|
|
#5 (permalink) |
|
Membre régulier
![]() Date d'inscription: février 2003
Messages: 136
|
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). |
|
|
|
|
![]() |
![]() |
||
[TOMCAT] [JAAS] Extension de LoginModule dans Tomcat
|
||
| Outils de la discussion | |
|
|