Number of current visits
There are 58 visitors online.
|
|
JCMS 5.7 : Authentifications spécifiques avec les AuthenticationHandler > Empêcher l'authentification d'un membre JCMS avec un AuthenticationHandler
|
|
Empêcher l'authentification d'un membre JCMS avec un AuthenticationHandler
David Koss -
on 11/3/11 at 10:34 AM
Bonjour,
Je développe un AuthenticationHandler spécifique et je souhaite, dans certain cas, qu'il empêche totalement l'authentification. Mon problème, c'est que même si j’interromps la chaîne d'authentification en faisant directement un return sans appeler la méthode doChain(), il arrive que l'authentification soit quand même réalisée, car le login/password fourni via le formulaire de login peut correspondre à un Member existant et actif.
Ainsi, ce que j'imagine, sans en être sûr, c'est que mon AuthenticationHandler intervient après SimpleAuthenticationHandler qui a probablement déjà réalisé l'authentification. Comment invalider alors cette authentification ?
Merci d'avance pour votre réponse...
|
|
|
|
Olivier Jaquemet -
on 11/3/11 at 11:12 AM
Bonjour David,
Pour interrompre tous les autres handler, tu dois positionner ton AuthenticationHandler le plus tôt possible dans la chaine d'authentification.
Je te suggère de te positionner après le premier handler : LogginAuthenticationHandler, qui n'authentifie rien, mais fait uniquement du log :
public class MyAuthenticationHandler extends AuthenticationHandler implements PluginComponent {
private static final Logger logger = Logger.getLogger(MyAuthenticationHandler.class);
public MyAuthenticationHandler () {
super(LoggingAuthenticationHandler.getInstance().getOrder() + 1);
}
[...]
}
Tu peux observer la chaine d'authentification en activant les logs suivants dans log4j.xml, et en ouvrant le fichier de log jcms.log (qui fait apparaitre plus d'information que la console standard) :
<logger name="com.jalios.jcms.authentication.AuthenticationContext">
<level value="TRACE" />
</logger>
Toutes ces infos sont dans la discussion Interruption de la chaine d'authentification
|
|
|
|
David Koss -
on 11/3/11 at 11:55 AM
Salut Olivier,
Merci beaucoup pour ta réponse. J'avais pourtant fait des recherche sur le forum, mais je n'avais pas trouvé cette discussion qui répond effectivement à mon besoin.
Au début, en te lisant, j'imaginais me brancher plutôt après le SessionAuthenticationHandler, car j'avais peur que mon WebService d'authentification soit appelé plusieurs fois par session. Mais en fait, en y réfléchissant, mon AuthenticationHandler commence par tester s'il a des crendentials fournis et sinon, il appel directement doChain() et fait un return. Je peux donc me permettre de me positionner au début de la chaîne, de toute façon mon WebService ne sera appelé que lorsqu'un formulaire de login sera soumis par l'utilisateur...
J'ai bon, non ?
|
|
|
|
Olivier Jaquemet -
on 11/3/11 at 12:10 PM
C'est exact.
Attention cependant qu'en faisant cela (ton code de webservice+return si credential, autrement doChain+return), tu n'interdit pas complètement tous les autres mode d'authentification.
Par exemple l'authentification par cookie est vérifié à chaque requête, indépendamment des credentials fournis.
si tu veux interdire complètement les autres modes d'authentification, sauf celui par session, plusieurs approches possibles :
- se positionner après celui par session, et vérifier le loggedMember avant/après l'appel de la chaine, et si un autre handler l'a positionné, le mettre à null (comme ça, seul la session fonctionnera)
- supprimer les autres handlers, mais c'est un peu bourrin comme approche
Tu peux donc faire ton authentification webservice si credential fourni, et rien d'autre. Mais autrement invoquer la chaine avec une vérification du résultat.
|
|
|
|
David Koss -
on 11/3/11 at 6:26 PM
Petite note d'après mon expérience du jour...
J'appel mon AuthenticationHandler custom en début de chaîne, celui-ci commence par vérifier qu'on a pas déjà un membre connecté et qu'en plus des credentials sont fournis. Car le but de mon AuthenticationHandler est d’intercepter le login/password saisi par l'utilisateur dans la page ou une portlet de login JCMS, soumettre ces éléments à un WebService, puis faire un setLoggedMember et continuer la chaîne d'authentification si le WebService valide les credentials fournis ou au contraire stopper la chaîne sans rien faire si le WebService ne valide pas.
Jusqu'ici tout va bien, techniquement ça marche et, dans les cas nominaux l'utilisateur se retrouve effectivement authentifié sur JCMS.
Là où j'ai eu une expérience un peu bizarre, c'est qu'en bout de course l'utilisateur se retrouve à chaque fois sur la page de login par défaut du front JCMS. Et, cerise sur la bizarrerie, quand l'utilisateur navigue à ce stade, par exemple en cliquant sur un lien de retour à l'accueil, il se retrouve effectivement authentifié. Ce passage par la page de login parait donc injustifié. Et puis en plus j'imaginais que la page de login redirigeait forcément sur la page d'accueil à partir du moment où l'utilisateur était déjà identifié... Bref, c'est bizarre.
J'ai testé un truc qui a fini par me faire retomber sur mes pattes : juste après avoir fait un setLoggedMember, je fait un sendRedirect vers l'accueil. Et là... Oh magie ! Ça marche et l'utilisateur est bien authentifié.
J'en arrive à me dire que, peut-être, tout ou partie de la mécanique de redirection des pages de login est tributaire d'un éventuel passage par le SimpleAuthenticationHandler qui, peut-être, ne fait rien à partir du moment où il voit qu'il y a déjà un membre authentifié en amont dans la chaîne.
Qu'en pensez-vous ? Est-ce que mon hypothèse est totalement débile où est-ce qu'il y a de ça ? Est-ce juste un passage obligé quand on fait un AuthenticationHandler qui exploite des credentials fournis par un formulaire de login standard de réimplémenter une mécanique de redirect ?
|
|
|
|
Olivier Jaquemet -
on 11/3/11 at 7:28 PM
Effectivement, le SimpleAuthenticationHandler effectue une redirection après l'authentification par credential (que ça soit lui ou un autre handler qui en soit responsable) pour quitter la page d'authentification et suivre l'éventuel parametre de redirection reçu ou rafraichir la page sans la paramètre d'authentification.
Voici le code que tu peux mettre après ton authentificaiotn pour effectuer cette redirection :
// Perform afterLogin redirection
if (loggedMemberBefore != ctxt.getLoggedMember()) {
if (Util.notEmpty(ctxt.getRequest().getParameter("redirect"))) {
ctxt.sendRedirect(ctxt.getRequest().getParameter("redirect"));
} else {
AuthenticationManager authMgr = AuthenticationManager.getInstance();
ctxt.sendRedirect(ServletUtil.getAbsUrlWithUpdatedParams(ctxt.getRequest(),
new String [] { },
new String [] { },
new String [] { "redirect", "JCMS_",
authMgr.getLoginParameter(), authMgr.getPasswordParameter(),
authMgr.getPasswordParameter(), authMgr.getOpLoginParameter()
},
false /* no & */ ));x
}
}
Cependant tout me fait réfléchir sur ton besoin fonctionnel initial : pourquoi dois tu interdire l'authentification classique alors que le membre a un login/pass valable ? ne voudrait tu pas plutot désactiver le membre dans ce cas là ?
|
|
|
|
David Koss -
on 11/4/11 at 8:46 AM
Merci Olivier de confirmer mon hypothèse et de me fournir le code nécessaire pour réimplémenter la redirection normalement effectuée par le SimpleAuthenticationHandler.
Pour le fait de désactiver les comptes, j'y avais pensé, mais ça va te paraître bête, je n'ai pas trouvé comment fonctionne la mécanique d'activation/désactivation de compte. Ou plutôt... Je pense avoir compris que c'est lié à un mot de passe particulier à mettre à la place de l'original. Sauf que, si c'est bien ça, je n'aime pas trop l'idée de ce que j'aurais à faire pour réactiver un compte. Il faudrait en fait que je fasse un setPassword du mot de passe saisi par l'utilisateur à partir du moment où mon WebService valide son authentification... Je ne trouve pas que ça soit spécialement plus propre que de simplement accepter son authentification. En tout cas je n'était pas à l'aise avec cette solution...
|
|
|
|
David Koss -
on 11/4/11 at 8:52 AM
Au fait, dans le code que tu as remis dans ton message, à quoi correspond loggedMemberBefore ?
Ça m'intrigue, car comme tu dis que le SimpleAuthenticationHandler est sensé faire une redirection quand il y a eu authentification par credential, même dans un AuthenticationHandler précédent, je ne comprend pas pourquoi dans mon cas la redirection n'a pas lieu (puisque je fait bien un doChain après le setLoggedMember)...
|
|
|
|
Olivier Jaquemet -
on 11/4/11 at 8:56 AM
En effet pour désactiver un compte le code est le suivant :
Member mbrUpdated= (Member) member.getUpdateInstance()
mbrUpdated.disable(); // équivalent à update.setPassword(Member.DISABLED_PASSWORD);
mbrUpdated.performUpdate();
Et pour le réactiver il faut faire :
Member mbrUpdated= (Member) member.getUpdateInstance()
mbrUpdated.setPassword(channel.crypt(newPassword));
mbrUpdated.performUpdate();
Si tu fais entièrement confiance à ton WebServices et qu'il a validé l'authentification, cela me parait tout à fait correct de le réactiver à partir du mot de passe reçu.
Mais si tu ne compte pas autoriser l'authentification JCMS standard, tu peux même mettre un mot de passe généré aléatoirement, de cette façon le compte est actif, mais seul l'authentification via WebService est possible (ce qui est fait pour l'activation d'un compte LDAP, cf custom/LdapAuthenticationHandler.java). Il n'y a aucune possibilité que l'utilisateur puisse se connecté avec ce mot de passe généré aléatoirement qui n'est pas enregistré crypté, pas d'appel à channel.crypt(password), ce qui ne correspondra donc à aucune HASH de mot de passe valide.
mbrUpdated.setPassword(PasswordGenerator.generateRandomPassword(8));
|
|
|
|
Olivier Jaquemet -
on 11/4/11 at 9:01 AM
Le code du SimpleAuthenticationHandler (simplifié)
Member loggedMemberBefore = ctxt.getLoggedMember();
ctxt.doChain();
// Perform afterLogin redirection
[...]
|
|
|
|