Je me demande quelle est l’approche actuelle en ce qui concerne l’authentification des utilisateurs pour une application Web utilisant JSF 2.0 (et des composants existants) et Java EE 6 (connexion/vérification des autorisations/logouts) avec des informations utilisateur dans une entité JPA: Le tutoriel Oracle Java EE est un peu clairsemé à ce sujet (ne traite que les servlets).
Ceci est sans en utilisant un tout autre framework, comme Spring-Security (acegi), ou Seam, mais en essayant de coller avec espoir à la nouvelle Java EE 6 (profil web) si possible.
Après avoir cherché sur le Web et essayé de nombreuses façons différentes, voici ce que je suggérerais pour Java EE 6:
Dans mon cas, j'avais les utilisateurs dans la base de données. J'ai donc suivi cet article de blog pour créer un royaume JDBC pouvant authentifier les utilisateurs en fonction du nom d'utilisateur et des mots de passe hachés MD5 dans ma table de base de données:
http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html
Remarque: la publication parle d'un utilisateur et d'une table de groupe dans la base de données. J'avais une classe User avec un attribut UserType enum mappé via des annotations javax.persistence à la base de données. J'ai configuré le domaine avec la même table pour les utilisateurs et les groupes, en utilisant la colonne userType comme colonne de groupe et tout s'est bien déroulé.
Toujours en suivant le blog ci-dessus, configurez votre web.xml et Sun-web.xml, mais au lieu d'utiliser l'authentification BASIC, utilisez FORM (en fait, peu importe celui que vous utilisez, mais j'ai fini par utiliser FORM). Utilisez le code HTML standard, pas le fichier JSF.
Ensuite, utilisez le conseil de BalusC ci-dessus sur l’initialisation paresseuse des informations d’utilisateur de la base de données. Il a suggéré de le faire dans un haricot géré en extrayant le principal du contexte des visages. Au lieu de cela, j'ai utilisé un bean de session avec état pour stocker les informations de session pour chaque utilisateur. J'ai donc injecté le contexte de session:
@Resource
private SessionContext sessionContext;
Avec le principal, je peux vérifier le nom d'utilisateur et, à l'aide du gestionnaire d'entité EJB, obtenir les informations sur l'utilisateur de la base de données et les stocker dans mon SessionInformation
EJB.
J'ai aussi regardé autour de moi pour trouver le meilleur moyen de me déconnecter. Le meilleur que j'ai trouvé utilise un Servlet:
@WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
public class LogoutServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(false);
// Destroys the session for this user.
if (session != null)
session.invalidate();
// Redirects back to the initial page.
response.sendRedirect(request.getContextPath());
}
}
Bien que ma réponse soit très tardive compte tenu de la date de la question, j'espère que cela aidera d'autres personnes qui se retrouvent ici chez Google, tout comme je l'ai fait.
Ciao,
Vítor Souza
Je suppose que vous voulez authentification basée sur un formulaire using descripteurs de déploiement et j_security_check
.
Vous pouvez également le faire dans JSF en utilisant simplement les mêmes noms de champs prédéfinis j_username
Et j_password
, Comme indiqué dans le didacticiel.
Par exemple.
<form action="j_security_check" method="post">
<h:outputLabel for="j_username" value="Username" />
<h:inputText id="j_username" />
<br />
<h:outputLabel for="j_password" value="Password" />
<h:inputSecret id="j_password" />
<br />
<h:commandButton value="Login" />
</form>
Vous pouvez effectuer un chargement paresseux dans le User
getter pour vérifier si le User
est déjà connecté et si ce n’est pas le cas, vérifiez si le Principal
est présent dans la demande et, le cas échéant, , puis obtenez le User
associé à j_username
.
package com.stackoverflow.q2206911;
import Java.io.IOException;
import Java.security.Principal;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
@ManagedBean
@SessionScoped
public class Auth {
private User user; // The JPA entity.
@EJB
private UserService userService;
public User getUser() {
if (user == null) {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
if (principal != null) {
user = userService.find(principal.getName()); // Find User by j_username.
}
}
return user;
}
}
Le User
est évidemment accessible dans JSF EL par #{auth.user}
.
Pour vous déconnecter, faites un HttpServletRequest#logout()
(et définissez User
sur null!). Vous pouvez obtenir un descripteur de HttpServletRequest
dans JSF par ExternalContext#getRequest()
. Vous pouvez également simplement invalider la session.
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "login?faces-redirect=true";
}
Pour le reste (définition des utilisateurs, des rôles et des contraintes dans le descripteur de déploiement et le domaine), suivez simplement le tutoriel Java EE 6 et la documentation servletcontainer].
Update : vous pouvez également utiliser le nouveau Servlet 3.0 HttpServletRequest#login()
) pour effectuer une connexion par programme au lieu de j_security_check
Qui peut ne pas être accessible en soi par un répartiteur dans certains conteneurs de servlets. Dans ce cas, vous pouvez utiliser un formulaire JSF complet et un bean avec les propriétés username
et password
et une méthode login
qui ressemble à ceci:
<h:form>
<h:outputLabel for="username" value="Username" />
<h:inputText id="username" value="#{auth.username}" required="true" />
<h:message for="username" />
<br />
<h:outputLabel for="password" value="Password" />
<h:inputSecret id="password" value="#{auth.password}" required="true" />
<h:message for="password" />
<br />
<h:commandButton value="Login" action="#{auth.login}" />
<h:messages globalOnly="true" />
</h:form>
Et cette vue couvre le bean géré qui se souvient également de la page initialement demandée:
@ManagedBean
@ViewScoped
public class Auth {
private String username;
private String password;
private String originalURL;
@PostConstruct
public void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
if (originalURL == null) {
originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
} else {
String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);
if (originalQuery != null) {
originalURL += "?" + originalQuery;
}
}
}
@EJB
private UserService userService;
public void login() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
try {
request.login(username, password);
User user = userService.find(username, password);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(originalURL);
} catch (ServletException e) {
// Handle unknown username/password in request.login().
context.addMessage(null, new FacesMessage("Unknown login"));
}
}
public void logout() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.invalidateSession();
externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
}
// Getters/setters for username and password.
}
De cette façon, le User
est accessible dans JSF EL par #{user}
.
Il convient de mentionner qu’il est possible de laisser complètement les problèmes d’authentification au contrôleur frontal, par exemple. un serveur Web Apache et évalue HttpServletRequest.getRemoteUser () à la place, ce qui correspond à la représentation Java de la variable d'environnement REMOTE_USER. Cela permet également de consigner de manière sophistiquée des conceptions telles que l'authentification Shibboleth. Filtrage des demandes sur un servlet Le conteneur via un serveur Web est une bonne conception pour les environnements de production, mod_jk est souvent utilisé pour cela.
Le problème HttpServletRequest.login ne définit pas l'état d'authentification dans la session a été corrigé dans la version 3.0.1. Mettez à jour glassfish avec la dernière version et vous avez terminé.
La mise à jour est assez simple:
glassfishv3/bin/pkg set-authority -P dev.glassfish.org
glassfishv3/bin/pkg image-update