it-swarm.com.de

Durchführen der Benutzerauthentifizierung in Java EE / JSF unter Verwendung von j_security_check

Ich frage mich, was der aktuelle Ansatz in Bezug auf die Benutzerauthentifizierung für eine Webanwendung ist, die JSF 2.0 verwendet (und falls Komponenten vorhanden sind) und Java EE 6-Kernmechanismen (Login/Berechtigungen prüfen/Logouts) mit Benutzerinformationen, die in einer JPA-Entität gespeichert sind.Das Oracle Java EE-Tutorial ist etwas spärlich (behandelt nur Servlets).

Dies ist ohne die Verwendung eines ganz anderen Frameworks, wie Spring-Security (acegi) oder Seam, aber der Versuch, hoffentlich bei der neuen Java EE 6-Plattform zu bleiben (Webprofil) wenn möglich.

155
ngeek

Nachdem Sie das Web durchsucht und viele verschiedene Methoden ausprobiert haben, empfehle ich Folgendes für die Java EE 6-Authentifizierung:

Richten Sie den Sicherheitsbereich ein:

In meinem Fall hatte ich die Benutzer in der Datenbank. Daher bin ich diesem Blogbeitrag gefolgt, um einen JDBC-Realm zu erstellen, der Benutzer basierend auf Benutzernamen und MD5-Hash-Passwörtern in meiner Datenbanktabelle authentifizieren kann:

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

Hinweis: Der Beitrag handelt von einem Benutzer und einer Gruppentabelle in der Datenbank. Ich hatte eine User-Klasse mit einem UserType-Enum-Attribut, das über javax.persistence-Annotationen der Datenbank zugeordnet wurde. Ich habe den Realm mit der gleichen Tabelle für Benutzer und Gruppen konfiguriert, wobei die userType-Spalte als Gruppenspalte verwendet wurde, und es hat einwandfrei funktioniert.

Formularauthentifizierung verwenden:

Konfigurieren Sie weiterhin Ihre Datei web.xml und Sun-web.xml, und verwenden Sie statt der BASIC-Authentifizierung FORM (eigentlich ist es egal, welche Sie verwenden, aber am Ende habe ich FORM verwendet). Verwenden Sie das Standard-HTML, nicht das JSF.

Verwenden Sie dann den obigen Tipp von BalusC, um die Benutzerinformationen aus der Datenbank zu initialisieren. Er schlug vor, dies in einer verwalteten Bean zu tun, um den Principal aus dem Face-Kontext zu holen. Ich habe stattdessen ein Stateful-Session-Bean verwendet, um Sitzungsinformationen für jeden Benutzer zu speichern. Daher habe ich den Sitzungskontext eingefügt:

 @Resource
 private SessionContext sessionContext;

Mit dem Principal kann ich den Benutzernamen überprüfen und mithilfe von EJB Entity Manager die Benutzerinformationen aus der Datenbank abrufen und in meinem EJB SessionInformation speichern.

Ausloggen:

Ich habe mich auch nach dem besten Weg umgesehen, mich abzumelden. Das beste, das ich gefunden habe, ist die Verwendung eines Servlets:

 @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());
  }
 }

Obwohl meine Antwort in Anbetracht des Fragendatums sehr spät ist, hoffe ich, dass dies anderen Menschen hilft, die hier von Google gelandet sind, genau wie ich.

Ciao,

Vítor Souza

84

Ich nehme an, Sie möchten formularbasierte Authentifizierung mit Implementierungsdeskriptoren und j_security_check.

Sie können dies auch in JSF tun, indem Sie einfach dieselben vordefinierten Feldnamen j_username Und j_password Verwenden, wie im Lernprogramm gezeigt.

Z.B.

<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>

Sie können den Getter User auch faul laden, um zu überprüfen, ob User bereits angemeldet ist. Wenn dies nicht der Fall ist, prüfen Sie, ob Principal in der Anforderung vorhanden ist , dann holen Sie sich das User, das mit j_username verknüpft ist.

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;
    }

}

Das User ist in JSF EL offensichtlich über #{auth.user} Zugänglich.

Führen Sie zum Abmelden ein HttpServletRequest#logout() (und setzen Sie User auf null!) Aus. Sie können ein Handle des HttpServletRequest in JSF durch ExternalContext#getRequest() erhalten. Sie können die Sitzung auch einfach ganz ungültig machen.

public String logout() {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    return "login?faces-redirect=true";
}

Befolgen Sie für den Rest (Definieren von Benutzern, Rollen und Einschränkungen in Deployment-Deskriptor und Realm) einfach das Java EE 6-Lernprogramm und die Servletcontainer-Dokumentation auf die übliche Weise.


Update : Sie können auch das neue Servlet 3.0 HttpServletRequest#login() verwenden, um eine programmatische Anmeldung durchzuführen, anstatt zu verwenden j_security_check, Die in einigen Servletcontainern möglicherweise nicht per se von einem Dispatcher erreichbar sind. In diesem Fall können Sie ein vollwertiges JSF-Formular und ein Bean mit den Eigenschaften username und password und einer login -Methode wie folgt verwenden:

<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>

Und diese Ansicht umfasste Managed Bean, die sich auch die ursprünglich angeforderte Seite merkt:

@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.
}

Auf diese Weise kann in JSF EL über #{user} Auf User zugegriffen werden.

148
BalusC

Es sollte erwähnt werden, dass es eine Option ist, Authentifizierungsprobleme vollständig dem Front-Controller zu überlassen, z. einen Apache-Webserver und werten stattdessen HttpServletRequest.getRemoteUser () aus. Dies ist die Darstellung Java) für die Umgebungsvariable REMOTE_USER. Dies ermöglicht auch anspruchsvolle Anmeldedesigns wie die Shibboleth-Authentifizierung. Filtern von Anforderungen an ein Servlet container über einen web server ist ein gutes design für produktionsumgebungen, oft wird dazu mod_jk verwendet.

7
Matthias Ronge

Das Problem HttpServletRequest.login legt den Authentifizierungsstatus in der Sitzung nicht fest wurde in 3.0.1 behoben. Aktualisieren Sie glassfish auf die neueste Version und Sie sind fertig.

Das Aktualisieren ist ganz einfach:

glassfishv3/bin/pkg set-authority -P dev.glassfish.org
glassfishv3/bin/pkg image-update
4
Hans Bacher