jueves, 28 de abril de 2011

Autenticación en JSF

Como todos sabemos la seguridad se puede dividir en 3 aspectos:

  • Autenticación: que el usuario sea realmente quien dice ser.
  • Autorización: que el usuario pueda hacer solo lo que el tenga permiso de hacer.
  • Seguridad de Datos: que el usuario vea los datos que el realmente puede ver.

No todas las aplicaciones necesitan todas estos aspectos, por ejemplo una aplicación menor de una intranet no necesariamente necesita la seguridad de datos.

Vamos a atacar la autenticación. Con JSF podemos autenticar a nuestros usuarios de diferentes maneras.

Una forma es por medio del contenedor, todos los contenedores web manejan el concepto de seguridad y la posibilidad de autenticar a un usuario. Muchas aplicaciones solo necesitan la autenticación; por lo tanto lo podemos hacer a nivel contenedor. Este provee tres formas: basic, form-based, y client certificate. Veamos un ejemplo de autenticación basic, agregando las siguientes lineas al web.xml :








BASIC
UserDatabase





La autenticación básica trajo con sigo el concepto de realm. Un realm es un objeto que representa al usuario y su autenticación. Realm no es un concepto estándar y se implementa de forma diferente en los contenedores. Por ejemplo en Apache Tomcat es un simple archivo xml:














Otra posible autenticación es basado en formularios, esta solución fue creada antes de JSF y no es recomendada si la autenticación debe estar integrada a la aplicación:



FORM
UserDatabase

/faces/login.jsp
/faces/loginError.jsp




Otra posibilidad es integrar el login a nuestra aplicación. Vamos a profundizar esta opción que es la más utilizada.

Lo que deberíamos hacer es una aplicación la cual no se pueda acceder a ninguna pagina sin antes logearse, y si alguien intenta acceder a la pagina esta lo redireccionara a la pagina de login. En el login el usuario podrá logearse y así poder realizar sus tareas.

El concepto de servler filter, implementado en la versión 2.3 de servlet y redefinido en 2.4 permite operar sobre una request antes que esta sea procesada. Esto nos permite chequear si el usuario esta logueado cuando quiere acceder a alguna pagina. Entonces declaremos nuestro filtro en el web.xml:



Require that the user log in before accessing any page
other than the entry pages

ForcedLoginFilter
org.assembly.util.ForcedLoginFilter



ForcedLoginFilter
*.jsp
REQUEST
FORWARD



Y la clase ForcedLoginFilter es la siguiente:


package org.assembly.util;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class ForcedLoginFilter implements Filter {
private static final String LOGIN_JSP = "login.jsp";

public ForcedLoginFilter() {
}

private static boolean checkLoginState(ServletRequest request,
ServletResponse response) throws IOException, ServletException {
boolean isLoggedIn = false;
HttpSession session = ((HttpServletRequest) request).getSession(false);
UserBean managedUserBean = null;
// If there is a UserBean in the session, and it has
// the isLoggedIn property set to true.
if (null != session
&& (null != (managedUserBean = (UserBean) session
.getAttribute("UserBean")))) {
if (managedUserBean.isIsLoggedIn()) {
isLoggedIn = true;
}
}
return isLoggedIn;
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

boolean isLoggedIn = checkLoginState(request, response);

if (isRedirect((HttpServletRequest) request) && !isLoggedIn) {
String loginURI = LOGIN_JSP;

RequestDispatcher requestDispatcher = request
.getRequestDispatcher(loginURI);

// Force the login
requestDispatcher.forward(request, response);
return;
} else {
try {
chain.doFilter(request, response);
} catch (Throwable t) {
// A production quality implementation will
// deal with this exception.
}
}
}

private boolean isRedirect(HttpServletRequest request) {
String requestURI = request.getRequestURI();

return (!requestURI.contains(LOGIN_JSP));
}

@Override
public void destroy() {
// TODO Auto-generated method stub

}

@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub

}
}




Las clases que son filtros deben implementar la interfaz Filter. En este caso lo que hace el filtro es verificar que existe el usuario en la session y si la url es redirecciónable, dado que si al ir a login redireccionamos se formara un bucle infinito.

El objeto UserBean es el encargado de representar un usuario.


package org.assembly.util;

public class UserBean {

private String userName;

private String userPassword;

public UserBean(String userName, String userPassword) {
this.userName = userName;
this.userPassword = userPassword;
}

public boolean isIsLoggedIn() {
return true;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getUserPassword() {
return userPassword;
}

public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}

}





Entonces ahora debemos crear la pagina login.jsp que sera como la siguientes



<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

< html>
< head>
< meta http-equiv="Content-Type" content="text/html; charset=UTF-8">


Base de Contribuyentes


< /head>
< body background="black">











action="#{loginBean.validate}" />







< /body>

< /html>




Y luego debemos crear el managed bean que sera como este:


package org.assembly.util.login;

import javax.faces.context.FacesContext;
import org.assembly.util.UserBean;

public class LoginBean {

private String userName;

private String userPassword;

public String validate() {

// Validar si la contraseña y pass son correctas.
// si hay un error return null;

UserBean user = new UserBean(userName, userPassword);

FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.put("UserBean", user);

return "index";
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getUserPassword() {
return userPassword;
}

public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}

}



Luego debemos declarar en el face-config.xml nuestro bean y la url login.



loginBean
org.assembly.util.login.LoginBean
request

...


login
/login.jsp




Este fue un pequeño ejemplo de login en jsf.