Realización de autenticación de usuario en Java EE / JSF utilizando j_security_check

Me pregunto cuál es el enfoque actual con respecto a la autenticación de usuario para una aplicación web que utiliza JSF 2.0 (y si existen componentes) y los mecanismos centrales de Java EE 6 (inicio / verificación de permisos / desconexiones) con información de usuario contenida en un JPA entidad. El tutorial de Oracle Java EE es un poco escaso en esto (solo maneja servlets).

Esto es sin hacer uso de un marco completo diferente, como Spring-Security (acegi), o Seam, pero tratando de mantenerse con la nueva plataforma Java EE 6 (perfil web) si es posible.

Después de buscar en la Web y probar de muchas maneras diferentes, esto es lo que sugeriría para la autenticación de Java EE 6:

Configurar el reino de la seguridad:

En mi caso, tenía los usuarios en la base de datos. Así que seguí esta publicación de blog para crear un Reino JDBC que pudiera autenticar a los usuarios basados ​​en el nombre de usuario y contraseñas con hash MD5 en la tabla de mi base de datos:

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

Nota: la publicación habla sobre un usuario y una tabla de grupo en la base de datos. Tenía una clase de usuario con un atributo enum UserType mapeado a través de anotaciones javax.persistence a la base de datos. Configuré el reino con la misma tabla para usuarios y grupos, usando la columna userType como columna de grupo y funcionó bien.

Usar autenticación de formulario:

Siguiendo la publicación del blog anterior, configure su web.xml y sun-web.xml, pero en lugar de usar autenticación BASIC, use FORM (en realidad, no importa cuál use, pero terminé usando FORMULARIO). Use el HTML estándar, no el JSF.

A continuación, utilice la sugerencia de BalusC anterior sobre la inicialización lenta de la información del usuario de la base de datos. Sugirió hacerlo en un frijol administrado obteniendo el principal del contexto de rostros. Usé, en cambio, un bean de sesión con estado para almacenar información de sesión para cada usuario, así que inyecté el contexto de la sesión:

@Resource private SessionContext sessionContext; 

Con el director, puedo verificar el nombre de usuario y, usando el Administrador de Entidades EJB, obtener la información del Usuario de la base de datos y almacenarla en mi EJB SessionInformation .

Cerrar sesión:

También busqué la mejor manera de desconectarme. El mejor que he encontrado es el uso de 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()); } } 

Aunque mi respuesta es muy tarde considerando la fecha de la pregunta, espero que esto ayude a otras personas que terminan aquí en Google, tal como yo lo hice.

Ciao,

Vítor Souza

Supongo que quiere autenticación basada en formularios usando descriptores de implementación y j_security_check .

También puede hacer esto en JSF simplemente usando los mismos nombres predefinidos de campo j_username y j_password como se demostró en el tutorial.

P.ej

 


Puede hacer una carga diferida en el User getter para verificar si el User ya está conectado y si no, luego verificar si el Principal está presente en la solicitud y, si es así, obtener el User asociado con 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; } } 

El User es obviamente accesible en JSF EL por #{auth.user} .

Para HttpServletRequest#logout() haga una HttpServletRequest#logout() (¡y configure User null!). Puede obtener un control de HttpServletRequest en JSF por ExternalContext#getRequest() . También puede invalidar la sesión por completo.

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

Para el remanente (definir usuarios, roles y restricciones en el descriptor de implementación y el reino), solo siga el tutorial de Java EE 6 y la documentación del servletcontainer de la manera habitual.


Actualización : también puede usar el nuevo Servlet 3.0 HttpServletRequest#login() para hacer un inicio de sesión programático en lugar de usar j_security_check que puede no ser accesible por un operador en algunos servletcontainers. En este caso, puede usar un formulario JSF completo y un bean con propiedades de username y password , y un método de login que se ve así:

     

Y esta vista tiene un ámbito de beans gestionados que también recuerda la página solicitada inicialmente:

 @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 esta forma, el User puede acceder a JSF EL mediante #{user} .

Cabe mencionar que es una opción dejar por completo los problemas de autenticación al controlador frontal, por ejemplo, un servidor web Apache y evaluar el HttpServletRequest.getRemoteUser () en su lugar, que es la representación JAVA para la variable de entorno REMOTE_USER. Esto también permite el inicio de sesión sofisticado de diseños como la autenticación Shibboleth. El filtrado de solicitudes a un contenedor de servlets a través de un servidor web es un buen diseño para entornos de producción, a menudo se usa mod_jk para hacerlo.

El problema HttpServletRequest.login no establece el estado de autenticación en la sesión se ha solucionado en 3.0.1. Actualiza glassfish a la última versión y listo.

La actualización es bastante sencilla:

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