Solución:
Supongo que desea una autenticación basada en formularios utilizando descriptores de implementación y j_security_check
.
También puede hacer esto en JSF simplemente usando los mismos nombres de campo predefinidos j_username
y j_password
como se demuestra en el tutorial.
P.ej
<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>
Podrías hacer una carga diferida en el User
getter para comprobar si el User
ya ha iniciado sesión y, si no es así, compruebe si el Principal
está presente en la solicitud y, de ser así, obtenga 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;
}
}
los User
es obviamente accesible en JSF EL por #{auth.user}
.
Para cerrar la sesión, haga un HttpServletRequest#logout()
(y establecer User
a nulo!). Puede obtener un control de la 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 dominio), simplemente siga el tutorial de Java EE 6 y la documentación del servletcontainer de la manera habitual.
Actualizar: también puede utilizar el nuevo Servlet 3.0 HttpServletRequest#login()
para hacer un inicio de sesión programático en lugar de usar j_security_check
que pueden no ser accesibles per-se por un despachador en algunos contenedores de servlets. En este caso, puede usar un formulario JSF completo y un bean con username
y password
propiedades y un login
método que se ve así:
<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>
Y este bean administrado de ámbito de vista 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 manera el User
es accesible en JSF EL por #{user}
.
Después de buscar en la Web y probar de muchas formas diferentes, esto es lo que sugeriría para la autenticación de Java EE 6:
Configure el reino de seguridad:
En mi caso, tenía los usuarios en la base de datos. Así que seguí esta publicación de blog para crear un JDBC Realm que pudiera autenticar a los usuarios según el nombre de usuario y las contraseñas con hash MD5 en mi tabla de base de datos:
http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html
Nota: la publicación habla de un usuario y una tabla de grupo en la base de datos. Tenía una clase de usuario con un atributo de enumeración UserType asignado 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.
Utilice la autenticación de formulario:
Siguiendo la publicación de blog anterior, configure su web.xml y sun-web.xml, pero en lugar de usar la autenticación BÁSICA, use FORM (en realidad, no importa cuál use, pero terminé usando FORM). Utilice el HTML estándar, no el JSF.
Luego use el consejo de BalusC anterior sobre la inicialización perezosa de la información del usuario de la base de datos. Sugirió hacerlo en un bean administrado obteniendo el principal del contexto de caras. 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 de EJB, obtener la información del usuario de la base de datos y almacenarla en mi SessionInformation
EJB.
Cerrar sesión:
También busqué la mejor manera de cerrar la sesión. El mejor que he encontrado es usar 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 tardía considerando la fecha de la pregunta, espero que esto ayude a otras personas que terminan aquí desde Google, como lo hice yo.
Ciao,
Vítor Souza
Cabe mencionar que es una opción dejar completamente los problemas de autenticación al controlador frontal, por ejemplo, un servidor web Apache y evaluar HttpServletRequest.getRemoteUser () en su lugar, que es la representación JAVA para la variable de entorno REMOTE_USER. Esto también permite diseños de inicio de sesión sofisticados, 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.