Effectuer l'authentification de L'utilisateur dans Java EE / JSF en utilisant la vérification de sécurité j

je me demande Quelle est l'approche actuelle en ce qui concerne l'authentification de l'utilisateur pour une application web utilisant JSF 2.0 (et si des composants existent) et les mécanismes de base Java EE 6 (connexion/vérification des permissions/déconnexions) avec l'information utilisateur détenue dans une entité JPA. Le tutoriel Java EE D'Oracle est un peu clairsemé à ce sujet (il ne traite que les servlets).

C'est sans faisant usage de tout un autre cadre, comme ressort-sécurité (acegi), ou Couture, mais essayer de s'en tenir, si possible, à la nouvelle plate-forme Java EE 6 (profil web).

153
demandé sur BalusC 2010-02-05 14:45:35

4 réponses

après avoir cherché sur le Web et essayé de nombreuses façons différentes, voici ce que je suggérerais pour l'authentification Java EE 6:

mettre en place le domaine de la sécurité:

dans mon cas, j'avais les utilisateurs dans la base de données. J'ai donc suivi ce billet de blog pour créer un domaine JDBC qui pourrait authentifier les utilisateurs basés sur le nom d'utilisateur et les mots de passe MD5-hashé dans ma table de base de données:

http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html

Remarque: le post parle d'un utilisateur et un groupe table dans la base de données. J'avais une classe D'utilisateurs avec un attribut UserType enum mappé via javax.la persistance des annotations à 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 group et ça a bien fonctionné.

Use authentification form:

toujours en suivant l'article de blog ci-dessus, configurer votre web.xml et sun-web.xml, mais au lieu d'utiliser l'authentification de base, utilisez FORM (en fait, peu importe lequel vous utilisez, mais j'ai fini par utiliser FORM). Utilisez le HTML standard, pas le JSF .

puis utiliser le Conseil de BalusC ci-dessus sur lazy initialisant l'information de l'utilisateur à partir de la base de données. Il a suggéré de le faire dans un bean géré obtenir le principal du visage contexte. J'ai utilisé, à la place, un haricot de session stateful pour stocker des 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, en utilisant le Gestionnaire D'entités EJB, obtenir les informations D'utilisateur à partir de la base de données et stocker dans mon SessionInformation EJB.

Déconnexion:

j'ai aussi cherché la meilleure façon de me déconnecter. Le meilleur que j'ai trouvé est d'utiliser 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 mon la réponse est vraiment tardive compte tenu de la date de la question, j'espère que cela aidera d'autres personnes qui finissent ici à partir de Google, tout comme je l'ai fait.

Ciao,

Vítor Souza

84
répondu Vítor E. Silva Souza 2014-05-13 19:14:15

je suppose que vous voulez formulaire d'authentification basée sur les à l'aide 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 démontré dans le tutoriel.

E. G.

<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 pourriez faire le chargement paresseux dans le User getter pour vérifier si le User est déjà connecté et si non, vérifiez si le Principal est présent dans la requête et si oui, 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 faire un logout HttpServletRequest#logout() ( User à null!). Vous pouvez obtenir une poignée du HttpServletRequest dans JSF par ExternalContext#getRequest() . Vous peut aussi 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), il suffit de suivre le tutoriel Java EE 6 et la documentation servletcontainer de la manière habituelle.


mise à jour : vous pouvez également utiliser le nouveau Servlet 3.0 HttpServletRequest#login() pour faire un login programmatique au lieu d'utiliser j_security_check qui peut ne pas être en soi accessible à un répartiteur dans certains serveurs. Dans ce cas, vous pouvez utiliser un formulaire fullworthy JSF et un haricot avec username et password propriétés et une méthode login qui ressemblent à 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 scoped managed bean qui se souvient aussi 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.
}

ainsi le User est accessible en JSF EL par #{user} .

146
répondu BalusC 2017-06-22 14:30:15

il convient de mentionner qu'il s'agit d'une option permettant de laisser complètement les problèmes d'authentification au contrôleur frontal, par exemple un serveur web Apache et d'évaluer la requête HttpServletRequest.getRemoteUser () à la place, qui est la représentation JAVA pour la variable D'environnement REMOTE_USER. Cela permet également des conceptions de log in sophistiquées telles que l'authentification Shibboleth. Le filtrage des requêtes vers un conteneur servlet via un serveur web est une bonne conception pour les environnements de production, Souvent mod_jk est utilisé faire.

7
répondu Paramaeleon 2012-11-05 11:04:04

the issue Httpservlequest.login ne définit pas l'état d'authentification dans la session a été corrigé dans 3.0.1. Mettez glassfish à la dernière version et c'est fini.

mise à Jour est assez simple:

glassfishv3/bin/pkg set-authority -P dev.glassfish.org
glassfishv3/bin/pkg image-update
4
répondu Hans Bacher 2017-03-20 21:16:20