Java Authentication And Authorization Service API Master Seminar

Transcription

Java Authentication And Authorization Service API Master Seminar
JAAS
Java Authentication And Authorization Service API
Résumé
Avant Jaas (Java Authentication and Authorization Service) API, la sécurité en
Java était conçue seulement pour protéger l’utilisateur du monde extérieur. Cette protection repose sur la Java sandbox dont le role est de protéger le système local des
programmes hostils venant de l’extérieur. D’autres mécanismes et outils tels que les
signatures digitales et le cryptage assurent respectivement l’intégrité des données et la
confidentialité des messages. JAAS offre des mécanismes supplémentaires qui exigent
des permissions explicites pour chaque utilisateur avant de pouvoir exécuter certaines
opérations. L’API permet d’intégrer aux applications l es mécanismes de sécurité standards déjà existants comme Solaris NIS (Network Information Services), Windows NT,
LDAP (lightweight access directory protocol) ou Kerberos. Cette intégration se fait
d’une manière transparente et configurable. JAAS est donc une couche entre l’application et les autres éléments de sécurité de Java. En se basant sur un exemple simple, le
présent article montre comment configurer et utiliser JAAS.
Master Seminar
Advanced Software Engineering Topics
Prof. Jacques Pasquier-Rocha
University of Fribourg, Switzerland
Department of Informatics
Software Engineering Group
Author: Matthias Buchs
[email protected]
Supervisor: Ghita Kouadri Mostéfaoui
[email protected]
May 22, 2003
TABLE DES MATIÈRES
1
Table des matières
1 JAAS, Vue d’ensemble
2
2 Programmation avec JAAS
2.1 Login . . . . . . . . . . . . . . . .
2.1.1 LoginContext . . . . . . .
2.1.2 Subject . . . . . . . . . .
2.2 Code utilisateur . . . . . . . . . .
2.3 Administration JAAS . . . . . . .
2.3.1 Configuration des modules
2.3.2 Fichiers de Police . . . . .
2.3.3 Exécuter l’exemple . . . .
2.4 Programmation JAAS avancée . .
2.4.1 JAAS Callbacks . . . . . .
2.4.2 Ecrire un module login . .
. . .
. . .
. . .
. . .
. . .
login
. . .
. . .
. . .
. . .
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
4
4
4
5
5
5
7
8
9
9
10
3 Déroulement d’un Login
12
4 Conclusion
14
A JAASSimple
A.1 LoginAction.java
A.2 SimpleClient.java
A.3 java.policy . . . .
A.4 jaas.policy . . . .
A.5 JaasDemo.config
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
B JAASGUI
B.1 GUIClient.java . . . . . .
B.2 GUILoginModule.java . .
B.3 GUICallbackHandler.java .
B.4 NamePrincipal.java . . . .
B.5 InputFrame.java . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15
15
16
17
17
17
.
.
.
.
.
18
18
19
21
22
23
1 JAAS, VUE D’ENSEMBLE
1
2
JAAS, Vue d’ensemble
Cet article est basé sur un article online de ”Java World” [3], et sur le chapitre correspondant du livre ”Java Security” [1, chap. 15].
Une application utilisant JAAS fonctionne comme suit:
1. Le programme demande à l’utilisateur de se logger et obtient un objet login.
En terme de code c’est facile: Il suffit de créer une instance de LoginContext et d’invoquer la méthode login() sur cet objet.
L’administrateur du système est responsable pour les fichiers de configuration. Il peut
déterminer ce qui se passe quand un utilisateur veut se logger. Cela se fait avec une liste
de modules login. Chaque module peut être optionnel ou obligatoire. L’administrateur
peut aussi configurer les paramètres de l’authentification. L’utilisateur doit par exemple
insérer un mot de passe Windows NT correct.
2. Le programme exécute une méthode (doAs() ou doAsPrivileged()) avec l’objet de
login et le code qui doit être exécuté au nom de l’utilisateur comme paramètres.
3. Des parties du code de l’utilisateur sont en général sensibles pour la sécurité du système
(p. ex. lire des fichiers ou établir des connexions réseau). Elles éxigent donc des permissions spécifiques. Celles-ci sont données à un utilisateur authentifié.
La figure Fig:1 illustre l’architecture d’authentification de JAAS. Le code de la couche
application communique surtout avec LoginContext. C’est ce dernier qui s’occupe du processus login. Pour celà il utilise un ou plusieurs modules login. Ces modules peuvent faire
leur login utilisant des services existants comme des bases de données (Rdbms), un server
LDAP (Jndi, Java Naming and Directory Interface), l’environement (nom d’utilisateur, id
du groupe d’utilisateur etc.) du système d’exploitation (Unix WinNT) ou des périfériques
comme l’authentification biométrique.
Il est important de constater qu’avec JAAS on a deux sortes de fichiers de police. Le
code qui exécute l’authentification à besoin des permissions, qui peuvent changer d’après le
module login. Le code qui est exécuté au nom d’un utilisateur aura d’autres permissions en
générale plus restreintes.
Le programmeur fait deux choses utilisant JAAS: Il doit appeler la méthode login() pour
authentifier l’utilisateur et exécuter du code au nom de l’utilisateur. L’administrateur doit
configurer un ensemble de modules login, configurer un ensemble de fichiers de police JAAS
et installer l’environnement du programme correctement.
Dans les prochaines sections nous allons voir comment utiliser et configurer JAAS avec un
exemple simple. Un nom et un mot de passe sont demandés d’un utilisateur via une fenêtre.
Si ces données sont correctes l’utilisateur pourra voir quelques propriétés de l’environement
et le contenu du répertoir courant. Dans le cas d’echec de l’authentification ces action ne
seront pas autorisées.
1 JAAS, VUE D’ENSEMBLE
Fig. 1 – Architecture d’authentification de JAAS (tiré de [3])
3
2 PROGRAMMATION AVEC JAAS
4
try {
// Cr e a t e a L o g i n C o n t e x t f o r t h e a p p l i c a t i o n ” JaasGUI ” .
// Use GUICallbackHandler t o o b t a i n u s e r l o g i n i n f o r m a t i o n .
LoginContext l c = new LoginContext ( ”JaasGUI” ,
new GUICallbackHandler ( ) ) ;
// Log i n t h e u s e r .
lc . login () ;
// Perform L o g i n A c t i o n as t h e a u t h e n t i c a t e d u s e r .
S u b j e c t . d o A s P r i v i l e g e d ( l c . g e t S u b j e c t ( ) , new L o g i n A c t i o n ( ) , null ) ;
// Log o u t t h e u s e r .
lc . logout () ;
} catch ( L o g i n E x c e p t i o n ex ) {
...
}
Listing 1: Code pour le login et l’exécution d’une action privilégiée.
2
Programmation avec JAAS
Un programme qui utilise JAAS est divisé en deux parties: le code pour le login et le code
pour l’action propre à l’utilisateur.
2.1
Login
Listing 1 montre ce qu’il faut faire pour authentifier un utilisateur et exécuter du code
au nom de cet utilisateur: Premièrement il faut créer un objet de type LoginContext. Cet
objet est utilisé pour logger un utilisateur. Si tout va bien on peut exécuter du code au nom
de l’utilisateur connecté . Pour finir il faut faire un logout.
2.1.1 LoginContext
Les deux premières activités utilisent la classe LoginContext (javax.security.auth.login.LoginContext). Une instance de cette classe représente un contexte pour authentifier
un utilisateur. Le déroulement de cette authentification est spécifié dans un fichier de configuration. Le premier argument du constructeur définit quel bloc d’instructions dans le fichier
de configuration doit être pris.
Quelques types de configurations ont besoin de ”Callback”s. Par exemple pour demander le
nom et le mot de passe d’un utilisateur (Ce qui est le cas dans notre exemple). Après un
login correct on peut obtenir un objet de type Subject.
2.1.2
Subject
La classe Subject (javax.security.auth.Subject) est utilisée pour représenter un utilisateur authentifié. Chaque utilisateur est représenté par un tableau d’objets Principal qui
est encapsulé par cette classe. Un tableau est utilisé parce que un utilisateur peut être identifié
par plusieurs characteristiques. Des exemples sont le numéro AVS ou un nom de compte SUN
Solaris ou Windows NT. Un principal représente donc une propriété d’un utilisateur. Toutes
2 PROGRAMMATION AVEC JAAS
5
les propriétés (nécessaires) d’un seul utilisateur forment celui-ci. Elles sont encapsulées par
un sujet.
Les méthodes les plus utilisées sont doAs() et doAsPrivileged(). Elles permettent d’exécuter
du code (un objet de classe PrivilegedAction) au nom d’un utilisateur (un objet de classe
Subject).
2.2
Code utilisateur
Le code que nous exécutons dans notre exemple est le suivant:
package jaasdemo ;
import j a v a . i o . ∗ ;
import j a v a . s e c u r i t y . ∗ ;
public c l a s s L o g i n A c t i o n implements P r i v i l e g e d A c t i o n {
public Object run ( ) {
S t r i n g B u f f e r sb = new S t r i n g B u f f e r ( ) ;
sb . append ( ”\n” ) ;
sb . append ( ” Java . home : ”+System . g e t P r o p e r t y ( ” j a v a . home” )+”\n” ) ;
sb . append ( ” User . home : ”+System . g e t P r o p e r t y ( ” u s e r . home” )+”\n” ) ;
sb . append ( ” User . d i r : ”+System . g e t P r o p e r t y ( ” u s e r . d i r ” )+”\n” ) ;
F i l e f i l e = new F i l e ( System . g e t P r o p e r t y ( ” u s e r . d i r ” ) ) ;
File [ ] f i l e s = f i l e . l i s t F i l e s () ;
f o r ( int i = 0 ; i < f i l e s . l e n g t h ; i ++) {
sb . append ( ”\ t ”+ f i l e s [ i ] . getName ( )+” \n” ) ;
}
sb . append ( ”\n” ) ;
return sb . t o S t r i n g ( ) ;
}
}
Listing 2: Le code utilisateur qui a besoin de permissions supplémentaires.
La seule chose qui change pour le developeur c’est que la classe doit implémenter soit
l’interface PrivilegedAction soit PrivilegedExceptionAction. Quand LoginAction est
exécuté l’utilisateur doit avoir les permissions PropertyPermission et FilePermission appropriées.
2.3
2.3.1
Administration JAAS
Configuration des modules login
L’objet LoginContext est assez complexe. Il est construit pour supporter un ensemble de
modules login. Un module login est le code qui effectue l’authentification. Selon le module
cela est accompli en interagissant avec un utilisateur (demander le nom et le mot de passe)
ou en utilisant l’information déjà existante dans l’environnement de l’utilisateur.
Les modules login sont chargés dynamiquement. Au lieu d’appeler un module spécifique dans
le code, le contexte login consulte le fichier de configuration pour savoir quelles classes il doit
charger.
2 PROGRAMMATION AVEC JAAS
6
Il peut y avoir plusieurs modules login. Ils sont appelés l’un après l’autre lors de l’authentification. Chacun peut ajouter un ou plusieurs objets Principal au sujet courant (l’utilisateur
courant).
Voici un exemple de fichier de configuration:
JaasSimple {
com . sun . s e c u r i t y . auth . module . NTLoginModule r e q u i r e d ;
com . sun . s e c u r i t y . auth . module . JndiLoginModule o p t i o n a l debug=true ;
};
JaasGUI {
jaasdemo . j a a s g u i . GUILoginModule r e q u i r e d ;
};
Listing 3: Un fichier de configuration pour JAAS.
Ce fichier de configuration peut avoir un nom quelconque. Cet exemple contient deux Applications: ”JaasSimple” et ”JaasGUI”. Le nom du deuxième bloc (”JaasGUI”) est associé
avec le nom qui a été passé comme argument au constructeur du contexte login dans le listing
1. Une fois cette association faite, chaque classe de la liste est appelée dans l’ordre.
Chaque ligne dans un bloc a le format suivant:
classname control-flag [optional parameters];
”control-flag” peut être required, sufficient, requisite ou optional. Les paramètres
optionnels sont de la forme nom=valeur.
Login control flags
Avec plusieurs modules login on peut contrôler comment ils sont appelés:
required Ce module est toujours appelé et le test doit toujours être passé.
sufficient Si l’utilisateur passe le test d’authentification de ce module, aucun autre module
est appelé à part les required; l’utilisateur est suffisamment authentifié.
requisite Si l’utilisateur passe le test d’authentification de ce module, d’autres modules
sont aussi appelés mais ils peuvent échouer, sauf les required.
optional Le test peut échouer. Mais, si tout les modules sont optionnels l’utilisateur doit
passé au moins un test.
L’interaction entre ces flags est compliquée. Sauf dans des circonstances particulières il vaut
mieux les éviter. Listing 4 montre une combinaison dangereuse. Si le test du NTLoginModule
est passé JndiLoginModule n’est jamais appelé. C’est à dire qu’on ne peut pas garantir qu’un
utilisateur sera connecté au deuxième module et il n’aura donc pas toujours des principals
du deuxième module dans son sujet.
2 PROGRAMMATION AVEC JAAS
7
DangerousCombination {
com . sun . s e c u r i t y . auth . module . NTLoginModule s u f f i c i e n t ;
com . sun . s e c u r i t y . auth . module . JndiLoginModule r e q u i s i t e ;
};
Listing 4: Si le premier test est passé le deuxième module n’est jamais appelé.
2.3.2
Fichiers de Police
Après avoir écrit un fichier de configuration il faut écrire au moins deux fichiers de police pour l’application: un fichier de police JAAS qui donne aux utilisateurs des permissions
spécifiques basé sur comment ils sont authentifiés, et un fichier de police standard.
Ecrire un fichier de police JAAS
Un fichier de police JAAS est très similaire à un fichier de police standard: la syntaxe est
presque la même et les types de permissions sont exactement les mêmes. La seule différence
c’est qu’on doit spécifier un type Principal et un nom pour chaque élément. Le nom et le
lieu du fichier sont arbitraires.
Les entrées dans ce fichier sont valables pour tout code exécuté avec la méthode doAs()
qu’on a vu plus haut. Ce fichier donne chaque principal d’un sujet (passé comme paramètre
a doAs()) un ensemble de permissions. Voilà un exemple:
grant
c o d e b a s e ” h t t p : / /www− i i u f . u n i f r . ch / ”
signedBy ”mb”
P r i n c i p a l com . sun . s e c u r i t y . auth . NTUserPrincipal ” buchsmat ” {
p e r m i s s i o n j a v a . i o . F i l e P e r m i s s i o n ”<<ALL FILES>>” , ” r e a d ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” j a v a . home” , ” r e a d ” ;
permission java . u t i l . PropertyPermission ” user . d i r ” , ” read ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” u s e r . home” , ” r e a d ” ;
};
Listing 5: Un fichier de police JAAS.
Les entrées codebase et signedBy sont les mêmes que dans les fichiers de police standards, les deux sont optionnelles. Par contre il faut au moins avoir une entrée Principal. Et
il faut spécifier le nom de la classe et le nom du principal qui doit être authentifié.
2 PROGRAMMATION AVEC JAAS
8
Ecrire un fichier de police standard
Une application utilisant JAAS doit avoir certaines permissions. Le code qui crée un
contexte login et qui exécute doAs() ou doAsPrivileged() doit avoir au moins ces permissions:
grant {
p e r m i s s i o n j a v a x . s e c u r i t y . auth . AuthPermission ” c r e a t e L o g i n C o n t e x t ” ;
p e r m i s s i o n j a v a x . s e c u r i t y . auth . AuthPermission ”doAs” ;
p e r m i s s i o n j a v a x . s e c u r i t y . auth . AuthPermission ” d o A s P r i v i l e g e d ” ;
};
Listing 6: Un fichier de police standard pour l’utilisation avec JAAS.
Naturellement ce code aura besoin d’autres permissions selon ce qu’il fait. C’est pour ça
qu’il est souvent plus simple de lui donner la permission java.security.AllPermission.
Le code qui implémente un module login doit avoir la permission javax.security.auth.AuthPermission "modifyPrincipals".
2.3.3
Exécuter l’exemple
Voici les pas pour faire tourner l’exemple ”JaasGUI”:
1. Séparer le code en deux parties: Une pour le login et une pour l’action. Chez nous le
code pour le login sera dans le package jaasdemo.jaasgui.* et l’action (LoginAction)
dans le package jaasdemo.*. Cela nous permet de créer des fichiers de police séparés
pour le login et l’action.
2. Créer le fichier de configuration login.
Ici il faut décider quel module login on veut utiliser. Dans notre exemple nous utilisons jaasdemo.jaasgui.GUILoginModule que nous allons discuter plus tard. Le fichier
entier est imprimé dans la section B.2.
3. Créer un fichier de police JAAS. La section A.4 contient le fichier de police entier.
4. Créer un fichier de police standard. La section A.3 contient le fichier de police entier.
5. Pour lancer cette application il faut spécifier les arguments suivants:
–
–
–
–
–
Un classpath approprié
-Djava.security.manager pour assurer les tests d’accès.
-Djava.security.policy pour spécifier le fichier de police standard.
-Djava.security.auth.policy pour spécifier le fichier de police JAAS.
-Djava.security.auth.login.config pour spécifier le fichier de configuration
JAAS.
Pour notre application cela nous donne la commande suivante (Windows):
java -classpath ".;.\classes" \
-Djava.security.manager \
-Djava.security.policy=java.policy \
-Djava.security.auth.policy=jaas.policy \
-Djava.security.auth.login.config=JaasDemo.config \
jaasdemo.jaasgui.GUIClient
2 PROGRAMMATION AVEC JAAS
2.4
9
Programmation JAAS avancée
Dans cette section nous allons voir comment on peut utiliser des ”Callback”s pour obtenir
de l’information d’un utilisateur et comment écrire un module login.
2.4.1
JAAS Callbacks
Les modules login comme celui de Windows NT obtiennent toute l’information de l’environnement de l’utilisateur. D’autres comme GUILoginModule de notre exemple doivent
interroger l’utilisateur. Cela est fait par des Callbacks de JAAS.
Quand on construit un contexte login, on peut passer en paramètre au constructeur un
objet qui implémente l’interface javax.security.auth.callback.CallbackHandler. Cet
objet est envoyé aux modules login. Si ces modules ont besoin de l’information de l’utilisateur ils le font à travers cet objet. Ce dernier le fait en utilisant un ou plusieurs objet
javax.security.auth.callback.Callback, chaqu’un demandant une partie de l’information totale (p. ex. un mot de passe).
Une classe qui hérite de javax.security.auth.callback.CallbackHandler suit ce schéma:
package jaasdemo . j a a s g u i ;
import j a v a . i o . ∗ ;
import j a v a x . s e c u r i t y . auth . c a l l b a c k . ∗ ;
public c l a s s GUICallbackHandler implements C a l l b a c k H a n d l e r {
public void h a n d l e ( C a l l b a c k [ ] c a l l b a c k s )
throws IOException , U n s u p p o r t e d C a l l b a c k E x c e p t i o n {
f o r ( int i = 0 ; i <c a l l b a c k s . l e n g t h ; i ++) {
i f ( c a l l b a c k s [ i ] instanceof . . . ) {
. . . c = ( . . . ) callbacks [ i ] ;
// I n s e r e l ’ i n f o r m a t i o n a p p r o p r i e e dans l ’ o b j e t .
} e l s e i f ( c a l l b a c k s [ i ] instanceof . . . ) {
// e t a i n s i de s u i t e
} else {
throw new U n s u p p o r t e d C a l l b a c k E x c e p t i o n ( c a l l b a c k s [ i ] ,
” GUICallbackHandler ” ) ;
}
}
}
}
Listing 7: Le schéma d’une classe de type CallbackHandler.
Vous allez décider quels Callbacks sont supportés. Pour chaque type il y aura un ”if”. Il
y a un nombre de Callbacks déjà implémentés:
– ChoiceCallback
– ConfirmationCallback
2 PROGRAMMATION AVEC JAAS
–
–
–
–
–
10
LocalECallback
NameCallback
PasswordCallback
TextInputCallback
TextOutputCallback
En générale ces classes sont suffisantes.
2.4.2
Ecrire un module login
Admettons que nous avons un ensemble de noms d’utilisateur et de mots de passe dans
une base de données. Et nous aimerions que notre système d’authentification se base sur ces
données. Pour cela il faut implémenter un module login. Dans cette section nous allons voir
comment écrire un module login.
Le module login dans l’exemple demande un nom d’utilisateur et un mot de passe. Le
seul nom accepté est ”buchsmat” avec le mot de passe ”securepassword”.
Un module login doit implémenter l’interface javax.security.auth.spi.LoginModule. Ca
veut dire qu’il doit avoir les méthodes suivantes:
public void initialize(Subject s, CallbackHandler ch, Map sharedState, Map options)
Initialise le module login. L’objet s doit être sauvegardé; le module va insérer un ou
plusieurs Principal, peut être en utilisant le CallbackHandler pour obtenir l’information d’authentification. L’objet sharedState peut être utilisé pour sauvegarder des
résultats; options contient les options spécifiés dans le fichier de configuration login.
public boolean login()
Authentifie un utilisateur. Les informations sur l’utilisateur peuvent être obtenues de
l’environnement via des Callback. Si l’authentification est correcte true est retourné,
false sinon. Si un Callback lève une exception ou s’il y a un autre problème cette
méthode doit lever une LoginException.
public boolean commit()
Cette méthode est appelée seulement si tous les modules login (nécessaires) ont accepté
l’utilisateur. C’est ici qu’il faut ajouter les objets Principal à l’objet Subject.
public boolean abort()
Cette méthode est appelée si l’utilisateur ne peut pas être authentifié.
public boolean logout()
Cette méthode doit enlever tout les objets Principal qui ont été ajoutés dans la
méthode commit().
2 PROGRAMMATION AVEC JAAS
11
Pour écrire un module login il faut avoir une ou plusieurs classes qui implémentent l’interface Principal. Comme ces classes sont souvent spécifiques au module login, normalement
on écrit des classes Principal propres. Voici l’implémentation de l’exemple:
package jaasdemo . j a a s g u i ;
import j a v a . i o . ∗ ;
import j a v a . s e c u r i t y . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s NamePrincipal implements S e r i a l i z a b l e , P r i n c i p a l {
private S t r i n g name ;
public NamePrincipal ( S t r i n g name ) {
t h i s . name = name ;
}
public boolean e q u a l s ( Object a n o t h e r ) {
i f ( ! ( a n o t h e r instanceof NamePrincipal ) ) {
return f a l s e ;
}
return ( ( NamePrincipal ) a n o t h e r ) . name . e q u a l s ( name ) ;
}
public S t r i n g getName ( ) {
return name ;
}
}
Listing 8: Classe qui représente un utilisateur authentifié par un module login.
La section B.2 montre le module login de notre exemple.
3 DÉROULEMENT D’UN LOGIN
3
12
Déroulement d’un Login
La figure Fig 2 montre la séquence des appels de méthodes pendant un login correct de
l’exemple ”JaasGUI”. Tout au début une instance de GUIClient est créée. Ce constructeur
lui crée un objet GUICallbackHandler pour obtenir les informations de l’utilisateur plus
tard. Avec cette instance et la chaı̂ne de charactères ”JaasGUI” (le nom du contexte login)
un objet LoginContext est instancié. Cet objet va chercher un bloc de même nom dans le
fichier de configuration (JaasDemo.config). Ce bloc contient la liste de tous les modules login
et leurs paramètres qui doivent être utilisés pour l’authentification.
Puis le client invoque la méthode login() du contexte login. Ce qui se passe derrière les
décors c’est que le contexte login crée une instance de chaque module login qu’il a trouvé
dans le fichier de configuration (voir plus haut). La méthode initialize() est appelée pour
chaque instance suivie d’un appel de login(). En même temps un objet Subject est créé.
Chez nous GUILoginModule est le seul module login. Dans sa méthode login il va demander le
nom et le mot de passe de l’utilisateur via GUICallbackHandler. Si tout va bien le contexte
login appelle commit() de chaque module. Le notre ajoute un objet de classe NamePrincipal
au sujet (Subject). Justqu’ici l’authentification est finie et dans notre cas avec succès.
Le client récupère le sujet qui a été créé pendant le login. Il appelle la méthode statique
doAsPrivileged() de la classe Subject. Les paramètres sont le sujet (représentant l’utilisateur courant) et une instance de type PrivilegedAction ou PrivilegedExceptionAction.
Chez nous c’est la classe LoginAction. Suivant les permissions spécifiées dans le fichier de
police JAAS (jaas.policy) cette opération est exécutée ou pas. Voilà donc l’autorisation.
3 DÉROULEMENT D’UN LOGIN
13
Fig. 2 – La séquence des appels de méthodes pendant un login correct de l’exemple ”JaasGUI”.
4 CONCLUSION
4
14
Conclusion
JAAS permet d’authentifier des utilisateurs et de leurs donner des permissions à la base
de leurs identités. Cette API est très souple car elle utilise des fichiers de configuration qui
déterminent à l’exécution comment l’authentification est faite et quelles autorisations un utilisateur reçoit. Par une couche d’abstraction entre le code d’application et les implémentations
des algorithmes d’authentification et d’autorisation une grande indépendence ces deux couches
est atteinte. Ce système facilite la vie d’un programmeur, mais complique un peu celle d’un
administrateur.
A JAASSIMPLE
A
A.1
15
JAASSimple
LoginAction.java
package jaasdemo ;
import j a v a . i o . ∗ ;
import j a v a . s e c u r i t y . ∗ ;
/∗∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s L o g i n A c t i o n implements P r i v i l e g e d A c t i o n {
/∗ ∗
∗ @return t h e c o l l e c t e d i n f o r m a t i o n i n a <CODE>S t r i n g </CODE> form .
∗/
public Object run ( ) {
S t r i n g B u f f e r sb = new S t r i n g B u f f e r ( ) ;
sb . append ( ”\n” ) ;
sb . append ( ” Java . home : ” + System . g e t P r o p e r t y ( ” j a v a . home” ) + ” \n” ) ;
sb . append ( ” User . home : ” + System . g e t P r o p e r t y ( ” u s e r . home” ) + ” \n” ) ;
sb . append ( ” User . d i r : ” + System . g e t P r o p e r t y ( ” u s e r . d i r ” ) + ” \n” ) ;
F i l e f i l e = new F i l e ( System . g e t P r o p e r t y ( ” u s e r . d i r ” ) ) ;
File [ ] f i l e s = f i l e . l i s t F i l e s () ;
f o r ( int i = 0 ; i < f i l e s . l e n g t h ; i ++) {
sb . append ( ”\ t ” + f i l e s [ i ] . getName ( ) + ”\n” ) ;
}
sb . append ( ”\n” ) ;
return sb . t o S t r i n g ( ) ;
}
}
Listing 9: LoginAction.java
A JAASSIMPLE
A.2
16
SimpleClient.java
package jaasdemo . j a a s s i m p l e ;
import jaasdemo . ∗ ;
import j a v a x . s e c u r i t y . auth . ∗ ;
import j a v a x . s e c u r i t y . auth . l o g i n . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s S i m p l e C l i e n t {
public S i m p l e C l i e n t ( ) {
try {
LoginContext l c = new LoginContext ( ” J a a s S i m p l e ” ) ;
lc . login () ;
System . out . p r i n t l n ( S u b j e c t . d o A s P r i v i l e g e d ( l c . g e t S u b j e c t ( ) ,
new L o g i n A c t i o n ( ) , null
));
lc . logout () ;
} catch ( L o g i n E x c e p t i o n ex ) {
ex . p r i n t S t a c k T r a c e ( ) ;
System . e r r . p r i n t l n ( ” Login f a i l e d ! ” ) ;
System . e x i t ( −1) ;
}
System . out . p r i n t l n ( ” Login s u c c e e d e d ! ” ) ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
S i m p l e C l i e n t s i m p l e C l i e n t = new S i m p l e C l i e n t ( ) ;
}
}
Listing 10: SimpleClient.java
A JAASSIMPLE
A.3
17
java.policy
grant {
permission
permission
permission
permission
};
j a v a . u t i l . P r o p e r t y P e r m i s s i o n ”∗ ” , ” r e a d ” ;
j a v a x . s e c u r i t y . auth . AuthPermission ” c r e a t e L o g i n C o n t e x t ” ;
j a v a x . s e c u r i t y . auth . AuthPermission ” d o A s P r i v i l e g e d ” ;
j a v a x . s e c u r i t y . auth . AuthPermission ” m o d i f y P r i n c i p a l s ” ;
Listing 11: java.policy
A.4
jaas.policy
g r a n t P r i n c i p a l com . sun . s e c u r i t y . auth . NTUserPrincipal ” m a t t h i a s ” {
p e r m i s s i o n j a v a . i o . F i l e P e r m i s s i o n ”<<ALL FILES>>” , ” r e a d ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” j a v a . home” , ” r e a d ” ;
permission java . u t i l . PropertyPermission ” user . d i r ” , ” read ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” u s e r . home” , ” r e a d ” ;
};
g r a n t P r i n c i p a l jaasdemo . j a a s g u i . NamePrincipal ” buchsmat ” {
p e r m i s s i o n j a v a . i o . F i l e P e r m i s s i o n ”<<ALL FILES>>” , ” r e a d ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” j a v a . home” , ” r e a d ” ;
permission java . u t i l . PropertyPermission ” user . d i r ” , ” read ” ;
p e r m i s s i o n j a v a . u t i l . P r o p e r t y P e r m i s s i o n ” u s e r . home” , ” r e a d ” ;
};
Listing 12: jaas.policy
A.5
JaasDemo.config
JaasSimple {
com . sun . s e c u r i t y . auth . module . NTLoginModule r e q u i r e d ;
};
JaasGUI {
jaasdemo . j a a s g u i . GUILoginModule r e q u i r e d ;
};
JaasRemote {
jaasdemo . j a a s g u i . GUILoginModule r e q u i r e d ;
};
Listing 13: JaasDemo.config
B JAASGUI
B
B.1
18
JAASGUI
GUIClient.java
package jaasdemo . j a a s g u i ;
import jaasdemo . ∗ ;
import j a v a x . s e c u r i t y . auth . ∗ ;
import j a v a x . s e c u r i t y . auth . l o g i n . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 1
∗
∗/
public c l a s s GUIClient {
public GUIClient ( ) {
try {
LoginContext l c = new LoginContext ( ”JaasGUI” , new
GUICallbackHandler ( ) ) ;
lc . login () ;
System . out . p r i n t l n ( ( S t r i n g ) S u b j e c t . d o A s P r i v i l e g e d ( l c . g e t S u b j e c t ( )
,
new L o g i n A c t i o n ( ) , null ) ) ;
lc . logout () ;
} catch ( L o g i n E x c e p t i o n ex ) {
ex . p r i n t S t a c k T r a c e ( ) ;
System . e r r . p r i n t l n ( ” Login f a i l e d ! ” ) ;
System . e x i t ( −1) ;
}
System . out . p r i n t l n ( ” Login s u c c e d e d ! ” ) ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
GUIClient GUIClient = new GUIClient ( ) ;
}
}
Listing 14: GUIClient.java
B JAASGUI
B.2
19
GUILoginModule.java
package jaasdemo . j a a s g u i ;
import
import
import
import
import
java . u t i l . ∗ ;
javax . s e c u r i t y
javax . s e c u r i t y
javax . s e c u r i t y
javax . s e c u r i t y
. auth . ∗ ;
. auth . c a l l b a c k . ∗ ;
. auth . l o g i n . ∗ ;
. auth . s p i . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s GUILoginModule implements LoginModule {
private S u b j e c t s u b j e c t ;
private C a l l b a c k H a n d l e r c a l l b a c k H a n d l e r ;
private NamePrincipal n a m e P r i n c i p a l ;
private boolean debug ;
private S t r i n g userName ;
private boolean s u c c e e d e d = f a l s e ;
private boolean commitSucceeded = f a l s e ;
private Map s h a r e d S t a t e ;
public void i n i t i a l i z e ( S u b j e c t s u b j e c t ,
CallbackHandler callbackHandler ,
Map s h a r e d S t a t e , Map o p t i o n s ) {
this . subject = subject ;
this . callbackHandler = callbackHandler ;
debug = ” t r u e ” . e q u a l s I g n o r e C a s e ( ( S t r i n g ) o p t i o n s . g e t ( ” debug ” ) ) ;
}
public boolean l o g i n ( ) throws L o g i n E x c e p t i o n {
i f ( debug ) {
System . e r r . p r i n t l n ( ” GUILoginModule : l o g i n ” ) ;
}
NameCallback nameCallback = new NameCallback ( ” u s e r : ” ) ;
PasswordCallback p a s s w o r d C a l l b a c k =
new PasswordCallback ( ” password : ” , f a l s e ) ;
C a l l b a c k [ ] c a l l b a c k s = new C a l l b a c k [ ] { nameCallback ,
passwordCallback } ;
try {
callbackHandler . handle ( c a l l b a c k s ) ;
} catch ( E x c e p t i o n ex ) {
ex . p r i n t S t a c k T r a c e ( ) ;
throw new L o g i n E x c e p t i o n ( ” E r r o r w h i l e g e t t i n g u s e r i n p u t ! ” ) ;
B JAASGUI
20
}
userName = nameCallback . getName ( ) ;
s u c c e e d e d = v a l i d a t e U s e r ( userName , p a s s w o r d C a l l b a c k . getPassword ( ) ) ;
passwordCallback . clearPassword ( ) ;
return s u c c e e d e d ;
}
public boolean commit ( ) throws L o g i n E x c e p t i o n {
i f ( debug ) {
System . e r r . p r i n t l n ( ” GUILoginModule : commit” ) ;
}
i f ( ! succeeded ) {
userName = null ;
return f a l s e ;
}
n a m e P r i n c i p a l = new NamePrincipal ( userName ) ;
i f ( ! subject . g e t P r i n c i p a l s ( ) . contains ( namePrincipal ) ) {
s u b j e c t . g e t P r i n c i p a l s ( ) . add ( n a m e P r i n c i p a l ) ;
}
userName = null ;
commitSucceeded = true ;
return true ;
}
public boolean a b o r t ( ) throws L o g i n E x c e p t i o n {
i f ( debug ) {
System . e r r . p r i n t l n ( ” GUILoginModule : a b o r t ” ) ;
}
i f ( ! succeeded ) {
return f a l s e ;
} e l s e i f ( s u c c e e d e d && commitSucceeded ) {
logout () ;
} else {
succeeded = false ;
}
return true ;
}
public boolean l o g o u t ( ) throws L o g i n E x c e p t i o n {
i f ( debug ) {
System . e r r . p r i n t l n ( ” GUILoginModule : l o g o u t ” ) ;
}
s u b j e c t . g e t P r i n c i p a l s ( ) . remove ( n a m e P r i n c i p a l ) ;
n a m e P r i n c i p a l = null ;
userName = null ;
s u c c e e d e d = commitSucceeded = f a l s e ;
return true ;
}
private boolean v a l i d a t e U s e r ( S t r i n g userName , char [ ] password ) {
B JAASGUI
21
return ” buchsmat ” . e q u a l s ( userName ) &&
” s e c u r e p a s s w o r d ” . e q u a l s (new S t r i n g ( password ) ) ;
}
}
Listing 15: GUILoginModule.java
B.3
GUICallbackHandler.java
package jaasdemo . j a a s g u i ;
import j a v a . i o . ∗ ;
import j a v a x . s e c u r i t y . auth . c a l l b a c k . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s GUICallbackHandler implements C a l l b a c k H a n d l e r {
private S t r i n g userName ;
private char [ ] password ;
public synchronized void h a n d l e ( C a l l b a c k [ ] c a l l b a c k s ) throws
IOException ,
UnsupportedCallbackException {
NameCallback userName = null ;
PasswordCallback password = null ;
f o r ( int i = 0 ; i < c a l l b a c k s . l e n g t h ; i ++) {
i f ( c a l l b a c k s [ i ] instanceof NameCallback ) {
userName = ( NameCallback ) c a l l b a c k s [ i ] ;
} e l s e i f ( c a l l b a c k s [ i ] instanceof PasswordCallback ) {
password = ( PasswordCallback ) c a l l b a c k s [ i ] ;
} else {
throw new U n s u p p o r t e d C a l l b a c k E x c e p t i o n ( c a l l b a c k s [ i ] ) ;
}
}
InputFrame i n p u t = new InputFrame ( t h i s ) ;
i n p u t . s e t V i s i b l e ( true ) ;
while ( true ) {
try {
wait ( ) ;
break ;
} catch ( I n t e r r u p t e d E x c e p t i o n ex ) {
ex . p r i n t S t a c k T r a c e ( ) ;
}
}
B JAASGUI
22
i f ( userName ! = null ) {
userName . setName ( t h i s . userName ) ;
}
i f ( password ! = null ) {
password . s e t P a s s w o r d ( t h i s . password ) ;
}
}
synchronized void l o g i n ( S t r i n g userName , char [ ] password ) {
t h i s . userName = userName ;
t h i s . password = password ;
notify () ;
}
}
Listing 16: GUICallbackHandler.java
B.4
NamePrincipal.java
package jaasdemo . j a a s g u i ;
import j a v a . i o . ∗ ;
import j a v a . s e c u r i t y . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s NamePrincipal implements S e r i a l i z a b l e , P r i n c i p a l {
private S t r i n g name ;
public NamePrincipal ( S t r i n g name ) {
t h i s . name = name ;
}
public boolean e q u a l s ( Object a n o t h e r ) {
i f ( ! ( a n o t h e r instanceof NamePrincipal ) ) {
return f a l s e ;
}
return ( ( NamePrincipal ) a n o t h e r ) . name . e q u a l s ( name ) ;
}
public S t r i n g getName ( ) {
return name ;
}
}
B JAASGUI
23
Listing 17: NamePrincipal.java
B.5
InputFrame.java
package jaasdemo . j a a s g u i ;
import
import
import
import
j a v a . awt . ∗ ;
j a v a . awt . e v e n t . ∗ ;
j a v a x . swing . ∗ ;
j a v a x . swing . b o r d e r . ∗ ;
/∗ ∗
∗ <p>C o p y r i g h t : C o p y r i g h t ( c ) 2003 </p>
∗ <p>O r g a n i s a t i o n : DIUF</p>
∗ <P>14/05/2003</P>
∗ @author M a t t h i a s Buchs ( m a t t h i a s . b u c h s @ u n i f r . ch )
∗ @version 1 . 0
∗/
public c l a s s InputFrame extends JFrame {
private GUICallbackHandler cb ;
public InputFrame ( f i n a l GUICallbackHandler cb ) {
try {
UIManager . setLookAndFeel (
UIManager . getSystemLookAndFeelClassName ( ) ) ;
} catch ( E x c e p t i o n ex ) {
ex . p r i n t S t a c k T r a c e ( ) ;
}
t h i s . cb = cb ;
s e t T i t l e ( ” Login ” ) ;
JPanel cp = new JPanel (new BorderLayout ( ) ) ;
setContentPane ( cp ) ;
cp . s e t B o r d e r (new EmptyBorder ( 1 0 , 1 0 , 1 0 , 1 0 ) ) ;
JLabel nameLabel = new JLabel ( ” User : ” ) ;
f i n a l J T e x t F i e l d name = new J T e x t F i e l d ( 1 5 ) ;
JLabel pwdLabel = new JLabel ( ” Password : ” ) ;
f i n a l J P a s s w o r d F i e l d pwd = new J P a s s w o r d F i e l d ( 1 5 ) ;
pwd . setEchoChar ( ’ ∗ ’ ) ;
JPanel upper = new JPanel (new BorderLayout ( ) ) ;
JPanel l e f t = new JPanel (new GridLayout ( 2 , 1 ) ) ;
l e f t . add ( nameLabel ) ;
l e f t . add ( pwdLabel ) ;
JPanel r i g h t = new JPanel (new GridLayout ( 2 , 1 ) ) ;
r i g h t . add ( name ) ;
r i g h t . add (pwd) ;
upper . add ( l e f t , BorderLayout .WEST) ;
upper . add ( r i g h t , BorderLayout .EAST) ;
B JAASGUI
24
JPanel l o w e r = new JPanel (new FlowLayout ( ) ) ;
JButton ok = new JButton ( ” ok ” ) ;
ok . s e t F o c u s P a i n t e d ( f a l s e ) ;
ok . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) {
public void a c t i o n P e r f o r m e d ( ActionEvent e ) {
i f ( name . getText ( ) . e q u a l s I g n o r e C a s e ( ” ” ) ) {
return ;
}
cb . l o g i n ( name . getText ( ) , pwd . getPassword ( ) ) ;
setVisible ( false ) ;
dispose () ;
}
}) ;
l o w e r . add ( ok ) ;
cp . add ( upper , BorderLayout .CENTER) ;
cp . add ( lower , BorderLayout .SOUTH) ;
s e t D e f a u l t C l o s e O p e r a t i o n (DO NOTHING ON CLOSE) ;
pack ( ) ;
Dimension s c r e e n S i z e = T o o l k i t . g e t D e f a u l t T o o l k i t ( ) . g e t S c r e e n S i z e ( ) ;
s e t L o c a t i o n ( ( s c r e e n S i z e . width − getWidth ( ) ) / 2 ,
( screenSize . height − getHeight () ) / 2 ) ;
setResizable ( false ) ;
}
}
Listing 18: InputFrame.java
RÉFÉRENCES
25
Références
[1] Scott Oaks. ”Java Security” 2nd Edition. O’Reilly, 2001
[2] Jess Garms & Daniel Sommerfield. ”Professional Java Security”. Wrox Press, 2001
[3] John Musser & Paul Feuer. ”All that JAAS”.
http://www.javaworld.com/javaworld/jw-09-2002/jw-0913-jaas_p.html
[4] ”JAAS Authentication”.
http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/AcnOnly.html
[5] ”JAAS Authorization”.
http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/AcnAndAzn.html
[6] ”JAAS Reference Guide”.
http://java.sun.com/j2se/1.4/docs/guide/security/jaas/JAASRefGuide.html