Java Series – Authentication y Authorization en J2EE

Si hay algo de lo que no se pueden quejar las personas que desarrollan en web con asp.net es sobre lo fácil que es el manejo de seguridad a recursos protegidos (authorization) y lo intuitivo de los tipos de autenticación que ofrece .net (authentication), pero suele ocurrir que por razones laborales/educativas etc dan el salto a otra plataforma de desarrollo y oh! sorpresa… donde esta esa sección de configuración para proteger los accesos??? Y así empieza la odisea … El post de hoy expone justamente el manejo de seguridad en J2EE.
Para el ejemplo vamos a crear un proyecto web (El proyecto está con JSF 2) y vamos a definir directorios donde van a estar las páginas de administración para 2 roles de usuario: Administrador y Super Administrador, controlando el acceso al recurso que le corresponda. La estructura para el ejemplo es la siguiente:

Como se visualiza en el gráfico están claramente definidas las carpetas, en las cuales se supondría que están las páginas a las que sólo se puede tener acceso con ciertos privilegios.
Adicionalmente el proyecto cuenta con la página forbidden.xhtml que para el caso es la página a la que es redirigido el request cuando se trata de acceder a una sección protegida sin tener las credenciales requeridas, y la página index.xhtml que es la página de login.
El estándar casi por omisión para seguridad en J2EE es Spring Security, pero para alguien que viene de otra plataforma le introduce otro millón mas de conceptos (lo cual no es malo, sólo que a veces son tan amplios que nos apartan totalmente el camino de lo que realmente queremos implementar), así que desde la especificación de Servlets 2.3 tenemos un feature que permite interceptar los requests y de esa manera determinar quien está tratando de acceder a los recursos de la aplicación; este feature son los Filters.
Muchos son los casos de uso en los que nos sirve interceptar requests, pero vamos a enfocarnos en autenticacion y autorización.
Para definir un filtro simplemente, creamos una clase que implemente la interfaz javax.servlet.Filter, e implementamos los métodos que nos exige:

public void init(FilterConfig filterConfig);

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);

public void destroy();

Tenemos definido el filtro y para interceptar los requests tenemos que configurarlo en el web.xml y mapear las rutas en las que va actuar el mismo, para hacerlo colocamos la siguiente configuración:


    <filter>
        <filter-name>AuthAdminFilter</filter-name>
        <filter-class>demo.security.AuthAdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthAdminFilter</filter-name>
        <url-pattern>/administration/*</url-pattern>
    </filter-mapping>

El primer elemento filter-name es referencial, lo que si es importante es en filter-class colocar el nombre completo de la clase que está implementando el filtro, luego en el siguiente filter-name se hace referencia a la declaración anterior y colocamos la ruta que queremos que el filtro administre, al colocar * cualquier página que esté dentro de ese contexto será validada.
Para validar en el filtro si el usuario está autenticado y el rol que tiene, en la pantalla de login, una vez validadas las credenciales vamos a almacenar una variable de sesión con un objeto de tipo UserBean, que basicamente tiene el id del usuario y el rol:

        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext extContext = context.getExternalContext();
        String url = "";
        if(isAdmin(user, password))
        {
            url = extContext.encodeActionURL(
                    context.getApplication().getViewHandler().getActionURL
                    (context, "/administration/adminPanel.jspx"));
            extContext.getSessionMap().put(USER_KEY, new UserBean(user, "admin"));
            extContext.redirect(url);
            return;

        }
        if(isSuperAdmin(user, password))
        {
            url = extContext.encodeActionURL(
                    context.getApplication().getViewHandler().getActionURL
                    (context, "/superadmin/superAdminPanel.jspx"));
            extContext.getSessionMap().put(USER_KEY, new UserBean(user, "superadmin"));
            extContext.redirect(url);
            return;
        }

        }

En el código mostrado se valida si las credenciales son válidas, si es así lo redirige a la sección de administración correspondiente, y finalmente pone en una variable de sesión el objeto de usuario.

Como tenemos 2 secciones que queremos validar los requests vamos a crear 2 filtros, uno para validar si es “admin” y otro para validar si es “super admin”, por lo tanto configuramos otro filtro en el web.xml:


     <filter>
        <filter-name>AuthSuperAdminFilter</filter-name>
        <filter-class>demo.security.AuthSuperAdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthSuperAdminFilter</filter-name>
        <url-pattern>/superadmin/*</url-pattern>
    </filter-mapping>

Hasta ahora tenemos el mapeo de las url’s a los filtros que van a validar los request, ahora veamos el desarrollo de las clases que implementan el Filtro de los requests:

Filtro para Sección de Administradores

public class AuthAdminFilter implements Filter {

    private FilterConfig configuration;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.configuration = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY) == null) {
            ((HttpServletResponse) response).sendRedirect("../forbidden.jspx");
        } else {
            chain.doFilter(request, response);
        }

    }

    @Override
    public void destroy() {
        configuration = null;
    }

Filtro para Sección de Super Administradores

public class AuthSuperAdminFilter implements Filter {

    private FilterConfig configuration;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.configuration = filterConfig;
    }

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

        if (((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY) == null || !((UserBean)((HttpServletRequest) request).getSession().getAttribute(
                AuthBean.USER_KEY)).getRole().equals("superadmin") ) {
            ((HttpServletResponse) response).sendRedirect("../forbidden.jspx");
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        this.configuration = null;
    }

}

Ambas implementaciones son casi idénticas salvo por el caso de que en la de superadmin validamos que además de que exista en sesión la variable que nos indica que está autenticado, el usuario tenga el rol de “superadmin”, en caso que no cumpla la condición se lo redirige a la sección de acceso restringido.
Bueno, eso es todo en el post 😀 para ver el ejemplo pueden descargar el código fuente desde aquí, es un proyecto netbeans 6.9 con glassfish.

Saludos,
gish@c

Cuanto sabe Google de ti?

Alguna vez te has preguntado que hace google con todas las cosas que buscas???… si nadie se va a enterar que buscaste fotos de Megan Fox y le diste click a ese link vago xD … Pues la verdad es que Google retiene muchisima información de que es lo que haces en la web (resultados de busquedas, direcciones en mapas, sitios visitados, etc etc), ya sea que tengas o no una cuenta google… Pero si tienes una cuenta google y mientras ingenuamente revisas tu gmail, se te ocurre realizar una busqueda y mas aún si estas en tu pc donde siempres estas logoneado con el tipico check de “Remember me” puedes notar que al hacer la tipica busqueda en Google… estas “logoneado”

Y así el monstruo de los servicios en línea tiene todo tu historial de actividades… y si aún no lo crees pues ellos mismos te ofrecen una herramienta para ver tu historial web…
http://www.google.com/history/?hl=en

Y aqui el historial de cosas que he buscado entre ayer y hoy… como se puede ver al lado izquierdo están desde la búsqueda de imagenes, blogs, las noticias (que si leo en google news), en fin… todo el track de mi vida onlinePreocupado x tu privacidad??? xD les dejo un excelente add-on para el firefox (GoogleSharing) que no le permite a google hacernos track ya que nos hace de proxy y nos genera una identidad anónima mientras navegamos con el firefox!!! Si algún PRO se le puede encontrar al asunto es el típico: “Alguna vez encontré en google pero no recuerdo el link…” lo cual en lo personal me ha sucedido muchísimas veces y lo he encontrado en mi historial web de búsquedas xD

Así que tú decides en si compartes o no tus preferencias en la web.

Saludos,
gish@c