Esecuzione dell’autenticazione utente in Java EE / JSF utilizzando j_security_check

Mi chiedo quale sia l’approccio attuale per quanto riguarda l’autenticazione utente per un’applicazione web che fa uso di JSF 2.0 (e se esistono componenti esistenti) e meccanismi core Java EE 6 (accesso / controllo permessi / disconnessioni) con le informazioni dell’utente contenute in un JPA quadro. Il tutorial Oracle Java EE è un po ‘scarso su questo (gestisce solo servlet).

Questo è senza fare uso di un intero altro framework, come Spring-Security (acegi) o Seam, ma cercando di rimanere speranzoso con la nuova piattaforma Java EE 6 (profilo web) se ansible.

Dopo aver cercato sul Web e provato in molti modi diversi, ecco cosa suggerirei per l’autenticazione Java EE 6:

Imposta il regno della sicurezza:

Nel mio caso, ho avuto gli utenti nel database. Così ho seguito questo post del blog per creare un Realm JDBC che potesse autenticare gli utenti in base al nome utente e alle password con hash MD5 nella mia tabella di database:

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

Nota: il post parla di un utente e una tabella di gruppo nel database. Ho avuto una class User con un attributo enum UserType mappato tramite annotazioni javax.persistence nel database. Ho configurato il realm con la stessa tabella per utenti e gruppi, utilizzando la colonna userType come colonna del gruppo e ha funzionato correttamente.

Usa l’autenticazione del modulo:

Seguendo ancora il post del blog sopra, configura il tuo web.xml e sun-web.xml, ma invece di usare l’autenticazione BASIC, usa FORM (in realtà, non importa quale dei due usi, ma alla fine ho usato FORM). Usa l’HTML standard, non il JSF.

Quindi utilizzare il suggerimento di BalusC sopra in pigro inizializzando le informazioni utente dal database. Ha suggerito di farlo in un bean gestito ottenendo il principale dal contesto dei volti. Ho usato, invece, un bean di sessione stateful per memorizzare le informazioni di sessione per ciascun utente, quindi ho inserito il contesto di sessione:

@Resource private SessionContext sessionContext; 

Con il principale, posso controllare il nome utente e, utilizzando EJB Entity Manager, ottenere le informazioni sull’utente dal database e memorizzarle nel mio SessionInformation EJB.

Disconnettersi:

Ho anche cercato il modo migliore per uscire. Il migliore che ho trovato sta usando 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()); } } 

Anche se la mia risposta è davvero tardiva considerando la data della domanda, spero che questo aiuti le altre persone che finiscono qui da Google, proprio come ho fatto io.

Ciao,

Vítor Souza

Suppongo che tu voglia un’autenticazione basata su form utilizzando descrittori di distribuzione e j_security_check .

Puoi farlo anche in JSF semplicemente usando gli stessi nomi di campo j_usernamej_password j_username e j_password come dimostrato nel tutorial.

Per esempio

 


È ansible eseguire il caricamento lazy nel getter User per verificare se l’ User è già connesso e, in caso contrario, controllare se il Principal è presente nella richiesta e in tal caso, quindi ottenere l’ User associato a 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; } } 

L’ User è ovviamente accessibile in JSF EL da #{auth.user} .

Per disconnettersi fai un HttpServletRequest#logout() (e imposta User su null!). È ansible ottenere un handle di HttpServletRequest in JSF da ExternalContext#getRequest() . Puoi anche invalidare completamente la sessione.

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

Per il resto (definizione di utenti, ruoli e vincoli nel descrittore e nel dominio di distribuzione), basta seguire il tutorial di Java EE 6 e la documentazione di servlet container nel solito modo.


Aggiornamento : è anche ansible utilizzare il nuovo Servlet 3.0 HttpServletRequest#login() per eseguire un accesso programmatico anziché utilizzare j_security_check che potrebbe non essere raggiungibile da un dispatcher in alcuni servlet container. In questo caso puoi utilizzare un modulo JSF completo e un bean con le proprietà username e password e un metodo di login simile al seguente:

     

E questo bean gestito con scope vista che ricorda anche la pagina inizialmente richiesta:

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

In questo modo l’ User è accessibile in JSF EL di #{user} .

Va detto che è un’opzione per lasciare completamente i problemi di autenticazione al front controller, ad esempio un server Web Apache e valutare invece HttpServletRequest.getRemoteUser (), che è la rappresentazione JAVA per la variabile di ambiente REMOTE_USER. Ciò consente anche sofisticati progetti di accesso come l’autenticazione Shibboleth. Il filtraggio delle richieste a un contenitore servlet tramite un server Web è un buon progetto per gli ambienti di produzione, spesso viene utilizzato mod_jk per farlo.

Il problema HttpServletRequest.login non imposta lo stato di autenticazione in sessione è stato corretto in 3.0.1. Aggiorna glassfish alla versione più recente e il gioco è fatto.

L’aggiornamento è abbastanza semplice:

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