jueves, 13 de octubre de 2011

Acceso seguro a nuestra aplicación con Tapestry y Tomcat 6. Perfilado de páginas.

Al acceder a nuestra aplicación se deben proporcionar el usuario y la clave. Queremos que la clave del usuario viaje por la red encriptada de forma que si alguien tuviese acceso a ella no pueda utilizarla. Para ello crearemos un certificado, que instalaremos en nuestro servidor, que permitirá que el navegador y el servidor se comuniquen por un canal seguro. Este certificado, al ser creado por nosotros, no garantiza a los usuarios que seamos quienes decimos ser. Para ello es necesario que el certificado lo emita una entidad certificadora y hay que pagar por él. Obviamente si el usuario se conecta a una dirección, dominio, que él conoce como de confianza no debería necesitar nada más.

Configuración del puerto seguro en Tomcat 6.
Primero tenemos que instalar un certificado autofirmado en tomcat. Para ello, siguiendo con las instrucciones de Apache (http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html), creamos un almacén de claves desde Java, con la herramienta keytool (que encontraremos dentro del directorio bin de la distribución de Java):
keytool -genkey -alias tomcat -keyalg RSA
Al no indicar el archivo de salida se generara el archivo en el directorio del usuario (en el caso de Windows XP, por ejemplo, “C:\Documents and Settings\[usuario que accede al equipo]). Allí veremos el archivo “.keystore”.
Como indicaba antes, puesto que este certificado esta creado por nosotros, al acceder a nuestra aplicación el navegador informara al usuario de que no puede garantizar que somos quienes decimos ser con una especie de mensaje de error. Si, nuestro usuario, confía en nosotros (debería) puede instalar nuestro certificado en el navegador y no tener que ver de nuevo este tipo de mensajes.
Es recomendable, aunque no necesario, que al crear el certificado utilicemos el nombre de nuestro dominio como nombre del certificado. Si no, como sucede en el mensaje anterior, el navegador nos indicará que el certificado tampoco coincide con el sitio (“El certificado sólo es válido para nombreyapellidos”).
A continuación debemos configurar el puerto seguro en Tomcat. Vamos a la carpeta de instalación de Tomcat y editamos el archivo server.xml del directorio conf. Encontraremos, y si no lo crearemos, un conector comentado para el puerto 8443 (o para el protocolo https en otro puerto). Introducimos la localización del almacén de claves y la clave:

<Connector port="8443" protocol="HTTP/1.1" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" keystoreFile="C:/Documents and Settings/[usuario]/.keystore" keystorePass="clavealmacen" clientAuth="false" sslProtocol="TLS" />

Página de logín.
En mi caso el formulario para acceder a la aplicación no pertenece a ella si no que está ubicado en una página de la web. Para probar es suficiente con la página siguiente:

<html>
<form method="post" action="https://localhost:8443/UEM/Index">
        usuario <input type="text" id="idUsuario" name="idUsuario"/><br/>
        clave <input type="password" id="clave" name="clave"/>
        <input type="submit"/>
    </form>
</html>

En el atributo action del formulario indicamos el protocolo seguro (https), el puerto seguro que vamos a utilizar (8443), el contexto web de nuestra aplicación (UEM) y la página que recibirá nuestra petición en Tapestry (Index).

Index en Tapestry.
Tapestry requiere para cada una de sus páginas un archivo “.tml”, la página en sí, y una clase Java asociada a ella.
Nuestro Index no visualiza nada. En el caso de que el login sea correcto redirigiremos al usuario a la primera página de nuestra aplicación y en caso contrario le devolveremos a la página de login para que lo intente de nuevo.
El código de Index.tml es, simplemente:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
</html>

La parte del código de “Index.java” que nos interesa es la siguiente:   

@Inject
private RequestGlobals requestGlobals;
   
@SessionState(create=false)
private Usuario usuario;
       
Object onActivate() throws Exception {
        HttpServletRequest request = requestGlobals.getHTTPServletRequest();
        Usuario usuarioAutenticado = validarUsuario(request.getParameter("idUsuario"), request.getParameter("clave"));
            if (usuarioAutenticado != null) {
                usuario = usuarioAutenticado;
                HttpSession sesion = request.getSession(false);
                String idSesion = sesion.getId();
                String url =  requestGlobals.getHTTPServletResponse().encodeURL("http://localhost/ /Presentacion;jsessionid=" + idSesion);
                return new URL(url);
            }
            else
                return new URL("http://localhost/UEM/login.html");       
}
Tapestry buscará el método onActivate y lo ejecutará. Le hemos pedido, a través de la anotación @Inject, que nos proporcione acceso a la petición que hemos recibido, objeto HttpServletRequest (que Tapestry nos proporciona a través del servicio requestGlobals), y que nos proporcione un objeto de la sesión http (HttpSesion) del tipo Usuario. El usuario no está creado y le pedimos a Tapestry que no lo cree (create=false). Si el usuario y la clave no son correctos devolvemos al usuario a la página de acceso para que vuelva a introducir su usuario y su clave. Si el usuario y la clave son correctos creamos el objeto usuario y redirigimos al usuario a la primera página de nuestra aplicación (siempre es bueno sobreescribir la url con el identificador de sesión por si el usuario no tiene activadas las cooquies en su navegador). Simplemente con crear el objeto usuario (asignarlo al usuarioAutenticado del código) Tapestry lo almacena en la sesión (HttpSesion). Si sólo creamos este usuario en la página de acceso de nuestra aplicación podremos definir una clase “PaginaBase” con un código como el  siguiente:

@SessionState(create=false)
    private Usuario usuario;
Object onActivate()    throws Exception {
        if (usuario == null) return FinDeSesion.class;
        // Continuamos con la página actual
        return null;
    }

Si hacemos extender al resto de páginas de nuestra aplicación de ésta conseguiremos que automáticamente se valide si el usuario se logó o no en la aplicación cuando intente acceder a cada una de ellas. Si el usuario es nulo no se habrá logado o habrá perdido la sesión (por tiempo de inactividad) por lo que deberá volver a logarse. En caso contrario, al retornar null, Tapestry continuara con la ejecución de la página actual, sabiendo que el usuario esta validado.

Acceso a las páginas en función del perfil del usuario.
Una forma de proteger nuestras páginas en función del perfil del usuario es extender de la PaginaBase y sobreescribir o definir un nuevo método onActivate. Podemos, por ejemplo, crear  la PaginaBaseProfesor que deberán extender todas aquellas páginas a las que se puede acceder sólo si se es “Profesor” sobreescribiendo el método del siguiente modo:

@Override
    protected Object onActivate() throws Exception {
        Object pagina = super.onActivate();
        if (pagina != null)
            return pagina;
        Usuario usuario = getUsuario();
        if (!usuario.isProfesor() && !usuario.isAdministrador())
            return enviarMensaje("MSJ_NO_ACCESO");
        // Continuamos con la página
        return null;
    }

Si, además, a una página determinada se puede acceder sólo si se es docente de una determinada asignatura (parámetro que vendrá en la Request) podremos crear un nuevo onActivate en esa página que extienda de la PaginaBaseProfesor:

public Object onActivate(String codigoExamen, String codigoAsignatura) throws Exception {
        this.examen = examen;
        setCodigoAsignatura(codigoAsignatura);
        return validarDocencia();
}

Tapestry primero llamara al onActivate de la página padre, que a su vez debería llamar al de su padre, de tal modo que se verificará primero si el usuario está logado, a continuación si el usuario es un profesor o un administrador y, finalmente si el profesor es docente de esa asignatura.

No hay comentarios:

Publicar un comentario