Buscar
Social
Ofertas laborales ES
« Patrones de Software (Parte 3) | Main | Nuevas API de Sun »
jueves
ago162001

Autentificacrión manejada por el contenedor -Tomca


Autentificación manejada por el contenedor en Tomcat.

Autentificación manejada por el contenedor en Tomcat.


Fecha de creación: 16.08.2001

Revisión 1.0.1 (15.10.2002)

Alberto Molpeceres
al AT javahispano DOT org

Copyright (c) 2002, Alberto Molpeceres. Este documento puede ser distribuido solo bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la última versión se encuentra en /licencias/).



Palabras previas



El mayor trabajo de este artículo esta en la configuración más que en la programación, asi que hay poco que hacer, a parte de andar en unos cuantos ficheros XML para que Tomcat lleve a cabo la autentificación por nosotros como manda la especificación de Servlets 2.2 (es de suponer que en las posteriores también, aunque sea con algún pequeño cambio). Este artículo esta enfocado a la version 3.2.x de Tomcat (la que yo uso), pero es totalmente aplicable a la serie 4.x (y a todo servidor que siga la especificación 2.2 de Servlets), salvo en algún aspecto que remarcaré en su momento.


Lo que importa, como he dicho, es la especificación de los Servlets. Tomcat 3.2.* sigue la especificación 2.2, mientras que Tomcat 4.* se esta ajustando a la 2.3, aunque esta aún se esta escribiendo, el trabajo se hace en paralelo. Pero en cuestiones de seguridad la especificación parece que no cambiará demasiado, por eso, la mayor diferencia entre Tomcat 3.* y Tomcat 4.* esta más en el diseño y trabajo interno que en la configuración. Lo único que puede cambiar son los nombres de las clases que realizan la autentificación, pero poco más, al menos nada que nos impida ver como hacerlo en la versión 4 u otro servidor.



La especificación de Servlets 2.2.



La especificación 2.2 de Servlets pone las directrices que los contenedores de Servlets tienen que ofrecer para ser compatibles. Una de estas directrices es la referente a la autentificación de usuarios para proteger los recursos de nuestra aplicación web. Esta directriz hará que sea el servidor el que se ocupe de proteger nuestros recursos de forma automática sin que nosotros escribamos una línea de código, solo tendremos que configurar los descriptores XML, tanto de Tomcat (para decidir el método de autentificación), como de la aplicación (para decidir que recusos son los protegidos).


No os podeis imaginar lo útil que puede ser esta funcionalidad que trae Tomcat (y todo servidor que siga la especificación 2.2 o posterior). El desarrollador de la aplicación web no tiene que preocuparse de nada referente a la autentificación, simplemente pone los enlaces entre las páginas que necesite. Será el encargado de desplegar la aplicación el que por medio del descriptor XML establezca que recursos son los protegidos, y Tomcat, el solito, se encargará de que el usuario se autentifique antes de dejarle acceder a ese recurso.


La especificación de Servlets 2.2 ofrece cuatro tipos de autentificación :


  • BASIC: Es la más sencilla. Simplemente nos saltará una ventana para que introduzcamos el usuario y la contraseña. Si damos un usuario válido pasaremos al recurso protegido, y si no, nos aparecerá una pantalla de error. Podemos definir una pantalla de error para ese tipo de error, pero el proprio cuadro que aparece rompe la estética de la aplicación, por lo que no se usa en aplicaciones de cara al cliente.



  • FORM: Lo que nos aparecerá en esta ocuasión será un formulario que nosotros diseñamos y definimos. Como siempre, si introducimos una pareja usuario-contraseña válida pasaremos al recurso protegido, y si no, nos aparecerá otra página de login erroneo que también podemos definir nosotros. Al estar tan metido dentro de la aplicación (son paginas web, como el resto de la aplicacion), es el método más usado.



  • CLIENT-CERT: Este método requiere un certificado del lado del cliente. Como os podeis imaginar, seguro si que es muy seguro, pero caro también. Sin comentarios.



  • DIGEST: Es un poco más avanzada que la BASIC, pero sigue sin ser espectacular, por lo que apenas se usa.


Lista 1: Tipos de autentificación definidos.


La especificación también define tres modos para indicar la seguridad del transporte de los datos a través de la Web.


  • NONE: Ningún tipo de cifrado, HTTP puro y duro.



  • CONFIDENTIAL: Esto significa SSL. Es decir, que nadie pueda ver ni cambiar lo que enviamos.



  • INTEGRAL: Esto significa que, aunque alguién pueda ver lo que se envia, se garantice que no se cambie por el camino. Normalmente los servidores usan para esto también SSL, aunque no es imprescindible.


Lista 2: Modos de transporte.


A lo largo del artículo indicaré donde hacer esto, pero en este ordenador no tengo instalado el módulo SSL, asi que no podré comprobarlo. Si alguién se anima, que me diga si funciona bien.


La especificación de Servlets (y de J2EE) basa la autentificación en dos aspectos : usuarios y roles. Son dos terminos claros, lo único que puede quedar poco claro es el termino rol, pero es simplemetne otra forma de llamar a los grupos de usuarios. La seguridad de J2EE esta basada en estos roles porque esta claro que en un sistema habrá un número mucho más pequeño de grupos que de usuarios, por lo que todo es más fácil si centramos la seguridad en los grupos.


Eso es todo lo que necesitamos saber de la especificación, asi que ahora pasemos a la acción.



Dominios de seguridad en Tomcat (fichero TOMCAT_HOME\conf\server.xml).



Tomcat ofrece dos dominios de seguridad (aunque nada impide crear los nuestros propios, en realidad Tomcat 4 ofrece ya un tercero). El termino anglosajón es Security Realms, que también se podría traducir como reino, pero a mi me gusta más dominio, aunque quizás en algún sitio lo leais traducido como reino. Estos dominios son en realidad dos clases que se encargan de recibir los datos del usuario al autentificarse y los contrasta con los datos que tiene Tomcat, en una caso contra un fichero XML (TOMCAT_HOME\conf\tomcat-users.xml), y en el otro contra cualquier base de datos que tenga un driver JDBC (o también contra un servidor LDAP con Tomcat4). Si CUALQUIER BASE DE DATOS, interesante, ¿no?.


En el caso del fichero TOMCAT_HOME\conf\tomcat-users.xml el formato se explica por si mismo:

<tomcat-users>
<user name="tomcat" password="tomcat" roles="administrador, usuario_ />
<user name="javahispano" password="javahispano" roles="usuario" />
</tomcat-users>



Realizar la autentificación por medio de una base de datos JDBC requiere un poco más de trabajo. La base de datos en cuestion requiere tres tablas para que funcione correctamente. Los nombres de las tablas y las columnas se pueden personalizar, pero debido a su uso tampoco hace demasiada falta. Esta información la podeis sacar del fichero TOMCAT_HOME\doc\JDBCRealm.howto, pero como siempre, los chicos de Tomcat están muy ocupados para ofrecernos este gran servidor y se les olvida decirnos como configurarlo ;-).


Suponemos que tenemos una base de datos con drivers JDBC disponible, en nuestro caso MySQL, y que hemos copiado el driver JDBC al directorio TOMCAT_HOME\lib (o al equivalente de nuestra aplicación si es la única que lo usa) para que Tomcat pueda encontrarlo y usarlo automáticamente.


La estructura de las tablas es la siguiente (la doy en código SQL porque supongo que todo el que este metido en estas labores puede entenderlo, y si no, solo tiene que copiarlo y ejecutarlo en su SGBD):


  • Una tabla de usuarios para almacenar los usuarios, con su nombre y password

    create table usuarios
    (
    nombre_usuario varchar(15) not null primary key,
    password_usuario varchar(15) not null
    );




  • Una tabla de roles que contendrá el nombnre de los mismos. Tomcat no usa esta tabla para nada (no se si lo hacen otros servidores), pero en fin, esta en la especificación, asi que_

    create table roles
    (
    nombre_rol varchar(15) not null primary key
    );




  • Una tabla que relaciona los usuarios con los roles.

    create table usuarios_roles
    (
    nombre_usuario varchar(15) not null,
    nombre_rol varchar(15) not null,
    primary key( nombre_usuario, nombre_rol )
    );



Lista 3: Tablas necesarias en la base de datos


Por supuesto ttenemos que rellenar esta tablas con valores, aqui un poco de código SQL para introducir los mismos valores que tiene el fichero XML.

insert into usuarios values ('tomcat', 'tomcat');
insert into usuarios values ('javahispano', 'javahispano');

insert into roles values ('administrador');
insert into roles values ('usuario');

insert into usuarios_roles values ('tomcat', 'administrador');
insert into usuarios_roles values ('tomcat', 'usuario');
insert into usuarios_roles values ('javahispano', 'usuario');



El fichero donde debemos decir a Tomcat que tipo de dominio de seguridad usar es el fichero TOMCAT_HOME\conf\server.xml, en la sección de RequestInterceptor para Tomcat 3 y en la sección Realm para Tomcat 4. Nosotros solo tenemos que configurarlo con nuestros datos concretos.


Por defecto Tomcat 3 viene configurado para usar el fichero XML para autentificar los usuarios. La parte que lo indica es la siguiente:

<!- Comprobar permisos usando el fichero xml. -->
<RequestInterceptor
className="org.apache.tomcat.request.SimpleRealm"
debug="0" />



Y Tomcat 4 no trae ninguno configurado por defecto, pero ejemplos para todos los posibles (dónde estarían cuando escribí este artículo por primera vez?!):

<Realm className="org.apache.catalina.realm.MemoryRealm" />



Inmediatamente despúes de estas etiquetas viene la que plantilla de como usar la autentificación contra una base de datos. Una lástima que nadie nos lo hubiera dicho ;-). Esta comentada, y lo único que tendremos que hacer nosotros es, primero comentar la anterior, y segundo descomentar esta y configurarla (en Tomcat 3) o descomentar la que nos interese (en Tomcat 4).

       <!-
Comprobar permisos usando base de datos

Otras opciones para driverName:
driverName="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
connectionName="javahispano"
connectionPassword="javahispano"

"connectionName" y "connectionPassword" son opcionales.
-->
<!--
En nuestro caso tenemos la siguiente configuración
(para MySQL):
Suponemos que a nuestra base de datos
-->

<RequestInterceptor
className="org.apache.tomcat.request.JDBCRealm"
<!--
El valor de debug indicara, como siempre, la
cantidad de informacion que saldra por la consola.
99 = toda, 0 = minima.
-->
debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://127.0.0.1/autentificacion?user=jh;password=jh"
userTable="usuarios"
userNameCol="nombre_usuario"
userCredCol="password_usuario"
userRoleTable="usuarios_roles"
roleNameCol="nombre_rol" />


      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/authority?user=test;password=test"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" />



En función de donde pongamos nuestra configuración (dentro de que elementos del fichero XML) estaremos configurando la seguridad para todo el servidor, para un servidor virtual, o para una única aplicación. Lo podeis ver en el propio archivo con los ejemplos, ahora no quiero mezclar más detallaes de ambos servidores.


Y asi, habiendo escogido uno y solo uno de los dominios de seguridad para que lo utilice Tomcat, podemos, por fin, pasar a usarlo en nuestra aplicación. En principio para hacer las pruebas podeis coger el que más rabia os de, funcionan los dos, y esto tiene que ver solo con la configuración de Tomcat, a nuestra aplicación le da bastante igual que sea una u otra, no tendremos que cambiar nada en ella.



Configurando nuestra aplicación.



Primero tenemos que crear nuestra aplicación de la forma que vimos en el artículo anterior [1]. Ahora no volvere a hacerlo, de modo que si no sabeis como hacerlo consultar como hacerlo en la sección articulos de nuestra web.


Tomcat (al menos la versión 3.2.*, desconozco la situación en la serie 4), solo tiene implementada la autentificación básica (BASIC) y la basada en formularios (FORM), asi que no os molesteis en probar las otras dos (CLIENT_CERT y DIGEST).


En este ejemplo, primero prepararemos nuestra aplicación para utilizar la autentificación básica, y después pasaremos a ver como hacerlo con formularios, que requiere un poco más de explicación, si bien casi todo el proceso es el mismo.


Autentificación BASIC.



Una vez creada la aplicación web pasamos a editar su descriptor de despliegue, que seguramente no será tan sencillo como este, pero para poner un ejemplo es suficiente, no usaremos ningún servlet, ni librerias de etiquetas JSP ni nada por el estilo.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

<display-name>Prueba autentificacion</display-name>
<description>
Estoy probando como funciona la
autentificación con Tomcat.
Alberto Molpeceres.
al@javahispano.com
</description>

<!--
Aqui empieza lo relativo a la autentificacion.
Son las llamadas restricciones de seguridad.
-->
<security-constraint>

<!--
Con tantos elementos web-resource-collection
como nos hagan falta vamos indicando las distintas
zonas.
-->
<web-resource-collection>
<web-resource-name>Nombre de la zona</web-resource-name>
<!--
Con url-pattern indicamos que ficheros o directorios
estan protegidos. Para nuestro ejemplo estarán TODOS
los ficheros de la aplicacion (se nos pedirá autentificarnos
nada mas intentar acceder a ella).
Podemos poner tantos elementos url-pattern como necesitemos.
-->
<url-pattern>/*</url-pattern>
<!--
Ahora indicamos los metodos por los que no se
puede acceder
-->
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>

<!--
Lo siguiente es indicar que roles tienen permitido
el acceso, en nuestro caso solo el administrador.
Tantos _role-name_ como necesitemos.
-->
<auth-constraint>
<role-name>administrador</role-name>
</auth-constraint>

<!--
Por fin indicamos el valor de seguridad en el
Transporte. Como dijimos, los posibles valores son :
NONE, CONFIDENTIAL, INTEGRAL.
Usare NONE porque ahora mismo no tengo instalado SSL.
-->
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>

<!--
Por ultimo indicamos el método por el que
queremos la autentificacion.
En este primer ejemplo, BASIC.
-->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Ejemplo de autentificacion basica</realm-name>
</login-config>

</web-app>



Listos. Ahora vamos al navegador e intentamos entrar a nuestra aplicación, veremos que pasa:

Figura 1: Cuadro de diálogo de autentificación básica.


(1)


Simplemente comprabar hasta que os aburrais que según nuestra configuración solo el usuario tomcat con contraseña tomcat puede entrar en la aplicación, ya que es el único usuario que tiene el rol administrador.


Pasemos ahora a algo más interesante: la autentificación por medio de un formulario.



Autentificación FORM.



Lo primero que tenemos que hacer es definir los formularios que utilizará nuestra aplicación, uno para el primer intento, y otro para el resto. Nada nos obliga a que sean dos distintos, pero siempre es mejor decirle al usuario que se ha equivocado al introducir sus datos en lugar de que piense que la aplicación no funciona. En realidad no es necesario que este segundo sea también un formulario HTML, puede ser una simple página que nos informe del error. Unos formularios de ejemplo, muy simples, podrían ser:


El fichero de login, login.html:

<html>
<head>
<title>
Login de aplicacion que requiere autentificacion
</title>
</head>
<body>
<h2>Login</h2>
<br>
<h3>www.javahispano.com</h3>
<br><br>
<form method="post" action="j_security_check">
Usuario: <input type="text" name="j_username"><br>
Password: <input type="text" name="j_password"><br>
</form>
</body>
</html>



Y el que se presentará cuando los datos son incorrectos, login_error.html:

<html>
<head>
<title>
Login de aplicacion que requiere autentificacion
</title>
</head>
<body>
<h2>Login</h2>
<br>
<h3>www.javahispano.com</h3>
<br><br>
<font color="#FF0000">
<h3>Datos incorrectos.<br></h3>
Por favor vuelva a intentarlo.
</font>
<br><br>
<form method="post" action="j_security_check">
Usuario: <input type="text" name="j_username"><br>
Password: <input type="text" name="j_password"><br>
<input type="submit" value="Login">
</form>
</body>
</html>



Tenemos que prestar atención en los distintos valores del formulario, ya que estos son estandar definidos en la especificación 2.2 para que las aplicaciones sean portables entre servidores.


El formulario tiene que enviarse por el método POST, y la acción a realizar será j_secutiry_check. Exactamente así escrita, todo en minúsculas (por si no lo sabeis, desde la version 3.2, Tomcat es sensible a mayúsculas incluso en Windows), y sin / antes. Es decir, /j_security_check y j_Security_Check son erróneos y no funcionarán. Ademas, los campos que tienen los datos del usuario deben llamarse j_username y j_password, no hay más vuelta de hoja, si quieres que Tomcat se encargue de la autentificación por ti, lo tienes que hacer así.


Ahora lo único que tenemos que cambiar respecto al ejemplo anterior es el elemento login-config del descriptor de nuestra aplicación, web.xml para que use la autentificación . Ahora será de la forma:

    <login-config>
<auth-method>FORM</auth-method>
<realm-name>
Ejemplo de autentificacion por medio de formulario
</realm-name>
<form-login-config>
<!--
Estamos suponiendo que hemos puesto los formularios
En el directorio raiz de nuestra aplicacion. Si no fuera
así abria que poner el path correcto.
-->
<form-login-page>/login.html</form-login-page>
<form-error-page>/login_error.html</form-error-page>
</form-login-config>
</login-config>



Tenemos que prestar atención al caso de querer proteger la aplicación entera y usar la autentificación con formulario (aunque no daría problemas con la básica), porque esto convertiría a nuestro formulario de entrada también en un recurso protegido, por lo cual Tomcat no podría mostrarlo y produciría un error. Si queremos hacer algo así, simplemente tendremos que meter nuestra aplicación en un directorio de acceso restringido y dejar los formularios fuera de él.




Apache y Tomcat tienen sus problemas.



Lamanteblemente os he mentido y no todo es así de sencillo, es posible que la primera vez que probeis la autentificación manejada por el servidor se os presenten algunos problemas. Pasaremos a intentar solucionarlos en esta sección, aunque lo dicho aquí no tiene valor alguno si se utiliza Tomcat en solitario (sin servidor Web).


Aunque Tomcat puede servir contenidos estáticos, normalmente lo usamos en conjunción con un servidor Web, muchas veces Apache, de forma que el servidor web se encarga de servir los contenidos estáticos, y Tomcat de los Servlets y de las páginas JSP. Esto se hace para liberar de trabajo a Tomcat, pero puede ser un problema a la hora de manejar la autentificación por medio del contenedor de Servlets.


El problema esta en que nosotros definimos las restricciones de seguridad en Tomcat, y algunas peticiones, las del contenido estático, no llegan a Tomcat, si no que las satisface el servidor Web directamente. Esto hace, que por ejemplo, la pagina secreta.html sea mostrada sin pedir la autentificación del usuario aunque este en un directorio declarado restringido por Tomcat.


Una solución sería transformar todas esas páginas a JSP, con solo cambiar la extensión de esas páginas sería suficiente. Otra solución sería decirle a Apache que le pasase TODAS las peticiones de nuestra aplicación a Tomcat (luego explico como se hace). Algunas personas dicen que Tomcat es lo suficientemente potente para soportar esto y más, pero tampoco es necesario, asi que veremos otra alternativa. Lo que nosotros vamos a hacer, es pasarle solo las peticiones de los recursos protegidos (por supuesto también todos los servlets y páginas JSP).


En el artículo anterior, sobre como instalar nuestras aplicaciones en Tomcat[1] vimos la forma de informar a Apache de que direcciones tenía que pasarle a Tomcat por tratarse con Servlets o JSP (viene al final del artículo). Ahora veremos como hacer para pasarle el resto de las cosas que necesitamos.


Lo que teníamos en el artículo anterior era algo asi como :

#
# Ahora informamos a Apache de que le mande las peticiones
# de Servlets y JSP a Tomcat.
# Solo le envia estas peticiones, ya que Apache sirve
# más rapidamente el contenido estatico.
# Ademas le decimos que protocolo (worker) usar, si tenemos el
# 13 mejor que 12 ;-).
# Ver el _como instalar Tomcat_ en nuestra web
# para masinformacion.
#
JkMount /mi_aplicacion/servlet/* ajp13
JkMount /mi_aplicacion/*.jsp ajp13



Lo que ahora tenemos que añadir aquí es el directorio o directorios que tenemos protegidos para que los administre Tomcat, sea contenido estático o dinámico. Simplemente añadimos una linea de la forma:

JkMount /mi_aplicacion/protegido/* ajp13



Para que Tomcat administrase toda la aplicación y sirviese tanto el contenido estático como el dinámico, deberiamos sustituir las líneas anteriores por una de la forma:

JkMount /mi_aplicacion/* ajp13



Y para terminar con este asunto, si Tomcat no se encarga de servir todas las peticiones a la aplicación, es decir, no utilizamos la línea anterior, tendremos que decirle a Apache que le pase a Tomcat otra dirección interesante, la que se encarga de autentificar a los usuarios, que como hemos visto anteriormente es j_security_check, asi que añadimos

JkMount /mi_aplicacion/j_security_check ajp13



Con esto esta todo, podemos configurar la relación Apache-Tomcat de la forma que más nos convenga, es problema del administrador decidir quién sirve qué.




Notas



(1) Como siempre mis textos estan en alemán, si alguién tiene gana de mandarlos en castellano, pues ya sabe, que me la envíe.

Recursos




[1] Instalación de aplicaciones en Tomcat.,
/articulos/ver_articulo.jsp?id=21


Acerca del autor

Alberto Molpeceres
Alberto es ahora mismo desarrollador de aplicaciones en ámbito cliente/servidor para la empresa T-Systems - debis Systemhaus en Munich (Alemania). Cuando no está trabajando o "metiendo caña" al resto de los integrantes de javaHispano intenta pasear con su novia, buscar la desaparecida lógica del idioma alemán o intentar olvidar la pesadilla que es buscar piso en Munich.

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.
Comentarios deshabilitados
Comentarios deshabilitados en esta noticia.