2016-03-14 9 views
6

Bei einem Authentifizierungsmechanismus vom Typ FORM, der für eine Java-Webanwendung definiert ist, wie erfassen Sie das Ereignis , das vor der Weiterleitung an die angeforderte Ressource ausgeführt wurde?? Gibt es irgendeine Art von Listener, in den ich meinen Code einfügen kann, wenn ein Benutzer sich anmeldet?Java EE-Authentifizierung: Wie protokolliert man ein Anmeldeereignis?

Ich denke, ein Filter ist nicht die beste Lösung, da der Filter mit der Ressource verknüpft ist und auch dann aufgerufen wird, wenn der Benutzer bereits authentifiziert ist und nach einer Ressource fragt. Ich frage mich, ob es eine Klasse/Methode gibt, die nur durch das Login-Ereignis ausgelöst wird.

Antwort

7

Es gibt kein solches Ereignis in Java EE. Noch. Als Teil von JSR375 wird die Container-verwaltete Sicherheit vollständig überarbeitet, da sie derzeit scattered über verschiedene Containerimplementierungen hinweg und nicht cross-container-kompatibel ist. Dies wird in dieser Java EE 8 Security API Präsentation dargestellt.

Es ist bereits eine Referenzimplementierung der Sicherheits-API in Bearbeitung, Soteria, die unter anderem von meinem Kollegen Arjan Tijms entwickelt wurde. Mit der neuen Security API wird CDI verwendet, um Authentifizierungsereignisse auszulösen, die Sie einfach @Observes. Die Diskussion der Spezifikation erfolgte in this mailing list thread. In Soteria ist es noch nicht konkret umgesetzt.

Bis dahin unter der Annahme FORM basierte Authentifizierung, wobei der Benutzerprinzipal intern in der Sitzung gespeichert ist, ist Ihre beste Wette manuell ein Servlet-Filter einchecken, wenn ein Benutzer Principal in der Anfrage während Ihrer Darstellung des angemeldeten Benutzers vorhanden ist fehlt in der HTTP-Sitzung.

@Override 
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { 
    HttpServletRequest request = (HttpServletRequest) req; 
    String username = request.getRemoteUser(); 

    if (username != null && request.getSession().getAttribute("user") == null) { 
     // First-time login. You can do your thing here. 
     User user = yourUserService.find(username); 
     request.getSession().setAttribute("user", user); 
    } 

    chain.doFilter(req, res); 
} 

Sie beachten Sie, dass auf /j_security_check einen Filter Registrierung ist nicht garantiert, als anständiger Container wird es handhaben innen zu arbeiten, bevor die ersten Filter betroffen sind, aus offensichtlichen Sicherheitsgründen (vom Benutzer bereitgestellten Filter kann die Anfrage in eine manipulieren schlechter Weg, entweder versehentlich oder bewusst).

Wenn Sie jedoch einen Java EE-Server verwenden, der den Undertow Servletcontainer verwendet, wie WildFly, dann gibt es eine sauberere Möglichkeit, seine internen Benachrichtigungsereignisse anzuhängen und dann benutzerdefinierte CDI-Ereignisse auszulösen.Dies ist in this blog von Arjan Tijms konkretisiert. Wie im Blog gezeigt, können Sie schließlich mit einer CDI Bean wie folgt enden:

@SessionScoped 
public class SessionAuthListener implements Serializable { 

    private static final long serialVersionUID = 1L; 

    public void onAuthenticated(@Observes AuthenticatedEvent event) { 
     String username = event.getUserPrincipal().getName(); 
     // Do something with name, e.g. audit, 
     // load User instance into session, etc 
    } 

    public void onLoggedOut(@Observes LoggedOutEvent event) { 
     // take some action, e.g. audit, null out User, etc 
    } 
} 
+0

Ich hasse SO dafür, dass ich nicht die Chance bekomme, mehr als einmal zu upvoten! Sagte, dass, Undertow-Lösung wird Ihre Webapp nicht vollständig portabel, das ist etwas, was ich gerade vermeiden möchte.An dieser Stelle ist das Hinzufügen eines Filters für eine Reihe von URLs wahrscheinlich die Lösung, die ich anwenden werde. Ich war nur etwas besorgt wegen des Overheads, der von einem so weit verbreiteten Filter kommen könnte, aber am Ende des Tages am meisten von der Zeit wird es nur zu einer nicht verifizierten if-Bedingung führen ... denkst du, es wäre ein "akzeptabler Overhead"? –

+0

:) SO hat das Konzept von [Bounties] (http://meta.stackexchange.com/questions/16065/how-does-the-boundy-system-work). Darüber hinaus haben einige Benutzer eine Wunschliste oder einen Link auf ihr Profil. Was den Overhead betrifft, ist das wirklich vernachlässigbar. Ein paar Nanosekunden vielleicht. Es gibt in einer vollwertigen Webanwendung viel bessere Dinge zu optimieren. – BalusC

0

Sie können den Servlet-Filter auf dem j_security_check URI verwenden. Dieser Filter wird nicht bei jeder Anforderung aufgerufen, sondern nur bei der Anmeldeanforderung.

Überprüfen Sie die folgende Seite - Developing servlet filters for form login processing - dies funktioniert in WebSphere App Server und im WebSphere Liberty-Profil.

Mit solchen Filter:

@WebFilter("/j_security_check") 
public class LoginFilter implements Filter { 
... 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
     System.out.println("Filter called 1: " +((HttpServletRequest)request).getUserPrincipal()); 
    chain.doFilter(request, response); 
     System.out.println("Filter called 2: " + ((HttpServletRequest)request).getUserPrincipal()); 

} 

die folgende Ausgabe ergibt:

// on incorrect login 
Filter called 1: null 
[AUDIT ] CWWKS1100A: Authentication did not succeed for user ID user1. An invalid user ID or password was specified. 
Filter called 2: null 

// on correct login 
Filter called 1: null 
Filter called 2: WSPrincipal:user1 

UPDATE

Andere mögliche Art und Weise, es zu tun ist Ihr eigenes Servlet für die Anmeldung zu verwenden, um die Aktion ändern auf Ihrer Anmeldeseite zu diesem Servlet und verwenden Sie request.login() Methode. Dies ist Servlet-API, sollte also auch in Wildfly arbeiten und Sie haben die volle Kontrolle über die Anmeldung. Sie müssen nur herausfinden, wie Wildfly die ursprünglich angeforderte Ressourcen-URL übergibt (WebSphere tut dies über einen Cookie).

Servlets Pseudocode:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    String user = request.getParameter("j_username"); 
    String password = request.getParameter("j_password"); 
    try { 
     request.login(user, password); 
     // redirect to requested resource 
    } catch (Exception e) { 
     // login failed - redirect to error login page 
    } 
+1

Das klingt eine gute Idee und in der Theorie sollte funktionieren, aber für mich ist es nicht, und ich weiß nicht warum. Der Filter scheint nicht durch den Aufruf 'j_security_check' ausgelöst zu werden. Zum Beispiel wird ein Filter mit 'url-pattern', der auf' * 'gesetzt ist, für jede ungeschützte Ressource aufgerufen, aber er wird nicht für eine Anmeldung mit falschem Passwort ausgeführt. Oder wird nur einmal ausgeführt (für die Ressourcen-URI, nicht "j_security_check" URI), wenn erfolgreich eingeloggt. Wildfly 8 hier. [relevanter Link] (http://www.theserverside.com/news/thread.tss?thread_id=32640) –

+0

@LuigiCortese Ich habe folgendes Servlet-Mapping '@WebFilter ("/j_security_check ")' verwendet und der Filter wird nur aufgerufen wenn Sie Ihr Anmeldeformular einreichen. Es funktioniert einwandfrei in WebSphere Liberty – Gas

+0

@BalusC Ihr Kommentar ist falsch oder falsch formatiert. Mein Filter wird korrekt auf dem j_security_check aufgerufen. Also verstehe ich nicht wirklich, was du mit "intern" meinst – Gas

Verwandte Themen