Encuesta

¿Cuál es tu interés en ejecutar lenguajes distintos de Java en la máquina virtual Java?

01-12-2008 - 193 votos

Controlando perfiles de usuario con Struts

14/05/2008 09:57 anonymous

La información que hay por Internet referente al tema es escueta y aunque se encuentran muchos artículos al final todos tienen un enlace a alguno de los dos siguientes, que son los únicos interesantes. Por supuesto, como no podría ser de otra manera, en inglés:

www.onjava.com/pub/a/onjava/2004/02/18/strutssecurity.html?page=last&x-order=date

www-128.ibm.com/developerworks/web/library/wa-appsec/?ca=dgr-
lnxw16ServletsTaglibsStrutsSecurity#listing2

Los dos los artículos lo primero que hacen es decir que struts no es una tecnología que lo haga todo, sino que pretende ser una plataforma básica abierta que sirva de base. Después te sueltan el royo de que en una aplicación que se precie se han de contemplar multitud de usuarios con diferentes roles y blablabla… y por fin pasan a dar la solución, que es similar.

Mi solución:

No me voy a poner a traducir ninguno de los artículos, sino que os voy a contar la solución que estoy adoptando, que no es que sea una cosa novedosa, es la misma que la de los artículos, sólo que explicada por mí (de hecho la solución no es mía, sino que es la que hemos adoptado en un proyecto que estoy realizando con más personas).

Consideraciones iniciales:

Lo primero que tenemos que tener en cuenta es que necesitamos un modo de identificación de usuarios en el sistema (login, password). Por este motivo supondremos para la realización de las restricciones que la identificación se ha realizado y que en session tenemos un objeto usuario (que será un registro – u objeto que represente dicho registro – de la tabla usuarios a la que haremos referencia posteriormente).

Como a priori no hemos definido ni los diferentes roles que tendrán los usuarios de la aplicación, ni las secciones en las que se dividirá nuestra aplicación ni los diferentes tipos de acceso que tendremos; y además, lo ideal sería que en caso de tener que modificar alguna de estas cosas fuera lo más rápido posible, la solución que vamos a ver consiste en tener una base de datos (o una serie de tablas dentro de la base de datos que usemos en la aplicación, si usamos alguna) donde almacenaremos toda esta información.

Además tendremos dos niveles de restricción, área de acceso (si el usuario puede o no acceder a la página) y tipo de operación (qué operaciones puede realizar el usuario de las disponibles en la página). Por ejemplo, si tenemos una página con una serie de datos editables habrá usuarios que no puedan acceder a ella, los habrá que puedan acceder pero no cambiar los datos (modo lectura) y los habrá que puedan acceder y cambiar los datos (modo lectura/escritura).

Requisitos de información:

Vamos a ver las tablas que necesitamos incluir en la base de datos y para qué sirve cada una. Evidentemente, dependiendo de los tipos de usuarios que se vallan a tener en la aplicación puede que tengamos que meter alguna tabla más o quitar alguna porque no sea necesaria; pero más o menos será de una forma parecida a esta.

- Usuarios

Entidad que recogerá los datos de los usuarios de la aplicación. En ella guardamos los datos personales de los usuarios, el identificador y la contraseña, y asociamos al usuario un perfil, que será el que defina los permisos.

Atributo Tipo BBDD Comentario
idUsuario (PK) numérico not null Código del usuario
login (UK) texto not null Login en la aplicación
password texto Null Contraseña del usuario
nombre texto Null Nombre del usuario
apellido texto Null Apellidos del usuario
... Null Resto de datos personales...
idPerfil (FK) numérico not null Id del Perfil que define los permisos del usuario.


PK: clave primaria UK: clave única FK: clave ajena

- Perfiles

Entidad que recogerá los perfiles de uso de la aplicación. Realmente la tabla es poca cosa, pero nos da un idPerfil que será el que usemos en los distintos permisos.

Atributo Tipo BD Comentario
idPerfil (PK) numérico not null Código del perfil
perfil texto not null Nombre del perfil


- Tipo de acceso

Entidad que recogerá los tipos de acceso, que pueden ser de creación, edición, eliminación, consulta...

Atributo Tipo BBDD Comentario
idTipoAcceso (PK) numérico not null Código del tipo de Acceso
codigo Numérico not null Código constante para identificar el tipo de acceso
tipoAcceso texto not null Descripción del tipo de acceso


- Secciones de la aplicación

Entidad que recogerá las distintas secciones de la aplicación sobre las que queremos definir permisos de acceso. Por ejemplo en una aplicación para una tienda podemos tener una zona de para el almacén, otra para los trabajadores de la tienda y otra para los clientes.

Atributo Tipo BBDD Comentario
idSeccionAplicacion (PK) numérico not null Código de la Sección de la Aplicación
codigo texto not null Código constante para identificar la sección
seccionAplicacion texto not null Descripción de la sección


- Permisos

Entidad que recogerá los permisos que los distintos perfiles tienen sobre las diferentes secciones. En esta tabla es donde asociamos el perfil con una sección de la aplicación y lo dotamos de una serie de permisos.

Atributo Tipo BBDD Comentario
idPermiso (PK) numérico not null Código del permiso
idTipoAcceso(FK) numérico not null Código del tipo de acceso
idSeccionAplicacion(FK) numérico not null Código de la sección de la aplicación
idPerfil(FK) numérico not null Código del perfil


Implementación de las restricciones sobre el sistema

Anteriormente hemos dicho que hay dos tipos de restricción, de acceso y de operación. Es ahora cuando realizaremos una diferenciación entre ellas, a la hora de implementarlas.

- Restricción de acceso

Pasando ya al lenguaje de struts, podemos entender una restricción de acceso como la posibilidad del usuario en cuestión de ver o no el resultado de un determinado Action, ya que se supone que antes de ver una página determinada se habrá de ejecutar el correspondiente Action, que es el que genera los datos a mostrar en la página.

Lo que queremos entonces es que si un usuario tiene permisos se ejecute el Action y se pase a la página jsp correspondiente; o que en el caso de no tener permisos pasemos a una página de error. Lo vamos a realizar en tres pasos:

1-. Superclase para los Action

Partiendo de que tenemos varias secciones en la aplicación y que cada sección se encuentra implementada por un conjunto de Actino, vamos a colocar sobre los Action de cada sección una superclase que en su método execute lo primero que compruebe sean los permisos de acceso del usuario. Sería una cosa más o menos así:

public abstract class RestriccionAction extends Action    
{

public ActionForward execute(.....)
{
doRestriccion();
return _execute();
}

public abstract ActionForward _execute(….);

private void doRestriccion(……) throws AccesoNoPermitidoException
{
/* Este metodo realiza las comprobaciones de acceso con
el perfil del usuario que haya en sesión */

}

}


De tal forma que los Action de la sección heredarán de este y será de la forma:

public class CuarquierCosaAction extends RestriccionAction    
{
public ActionForward _execute(.....)
{
/* Este es el método execute que tenía el Action, pero le cambiamos el nombre
para que se ejecute el de la clase padre que implementa la restricción */

}
}


2-. Extender ActionMapping

Extenderemos la clase ActionMapping de struts para darle más atributos, ya que interesará tener para cada Action datos relativos a la sección a la que pertenece, así que le añadiremos el atributo applicationZone.

public class SecurityActionMapping extends ActionMapping    
{
private String zone = null;

public String getApplicationZone()
{
return zone;
}

public void setApplicationZone(String newZone)
{
zone = newZone;
}
}


A partir de ahora, el parámetro de tipo ActionMapping del método execute de los Action será de tipo SecurityActionMapping (podremos hacer un casting dentro del método a un objeto de esta clase con el objeto de entrada). Pero para hacer que esto sea así, antes hemos de completar el siguiente paso.

3-. Inclusión del atributo en el struts-config.xml

En el fichero struts-config.xml modificamos las definiciones de los objetos Action incluyendo el atributo className=”ruta_de_paquetes.SecurityActionMapping” y una directiva set-property. De la siguiente forma

 path=”...” 
[...]
className="ruta_de_paquetes.SecurityActionMapping"
[...] >

property="applicationZone"
value="CODIGO_DE_SECCION" />

[...] />
>



El código de sección deberá coincidir el valor del atributo codigo de algún registro de la base de datos de la tabla de secciones de la aplicación.

- Restricción de operación

Ya hemos dicho que larestricción de operación se puede entender cómo la necesidad de un determinado perfil para disponer de determinadas opciones o controles en laspáginas JSP que se visualizan, como pueden ser permisos de edición o no de algunos datos. (por supuesto, si un usuario tiene restringido el botón editar – por poner un ejemplo – de una página jsp, también deberá tener restringida la ejecución del Action que realiza la edición, mediante una restricción de acceso)

Las restricciones de operación deberán tener reflejo en las páginas JSP que muestran la información y los controles para operar sobre ella; por ejemplo, si no se pueden editar los datos, los campos de edición deberán aparecer en sólo lectura y el botón de grabar debe estar oculto. Lo haremos en dos pasos:

1-. Colocación de permisos de operación en request

En el Action que implementa la restricción de acceso correspondiente se establecerán en ámbito de request los permisos de operación que el usuario que hay en session tiene en la sección del Action que se invocó para llegar hasta el JSP. Los permisos posibles dependerán realmente de cómo sea nuestra aplicación y de, por supuesto, los permisos que tenga el perfil del usuario.

Por ejemplo: Si el usuario tiene permisos para modificar datos, se ha de establecer en ámbito de request el atributo canEdit a true

request.setAttribute(“canEdit”, new Bolean(true));

2-. Restricción en las páginas JSP

En las páginas JSP se han de restringir la aparición o la funcionalidad de los diferentes controles dependiendo de los permisos que tengamos establecidos en request. Por ejemplo, si tenemos canEdit el botón de edición deberá aparecer y los campos ser editables; pero si no tenemos canDelete, el botón de borrar deberá permanecer oculto (por supuesto el usuario no podrá tener acceso al Action que realiza dicho borrado – mediante una restricción de acceso).

Anexo: De la excepción AccesoNoPermitidoException a la pantalla de error

Antes hemos usado la excepción AccesoNoPermitidoException, que sería de la forma:

public class AccesoNoPermitidoException extends Exception    
{
public void printStackTrace()
{
super.printStackTrace();
}
}


Esto lo que provoca es un fallo en la llamada al servidor, por lo que se devuelve la típica página de error. Evidentemente queremos que la página que se muestre sea distinta, una del estilo “no tiene permisos para realizar la acción solicitada. Contacte con el administrador...”. Además, de camino vamos a tener la posibilidad de tener nuestra propia página de error para la aplicación.

¿Cómo lo haremos? De una forma fácil: vamos a crear un manejador de excepciones, lo vamos a poner como manejador de excepciones de la aplicación y vamos a crear nuestra página de error y nuestra página de acceso no permitido.

El manejador de errores va a ser una clase del estilo:

public class ExceptionHandler extends ExceptionHandler    
{
public ActionForward execute(Exception exception, ExceptionConfig config,
ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws ServletException
{
if(exception.getClass().equals(AccesoNoPermitidoException.class))
{
return mapping.findForward("permisoDenegado");
}
else
{
request.setAttribute("exception",exception);
return mapping.findForward("error");
}
}
}


El método lo que ha de hacer es redireccionar al correspondiente forward (que posteriormente incluiremos en el struts-config.xml). Como queremos diferenciar entre un acceso no permitido y una excepción, según lo que sea (usando la condición if(exception.getClass().equals(AccesoNoPermitidoException.class)) vemos si es un acceso no permitido) redireccionamos a un sitio o a otro. Además, si es una excepción metemos en request dicha excepción, por si luego la queremos mostrar en la pantalla de error.

Para establecer nuestro manejador de excepciones como manejador por defecto incluimos en el struts-config.xml el siguiente código detrás de la sección form-beans:

>
key="lang.exception" type="java.lang.Exception"
handler="ruta_de_paquetes.ExceptionHandler" />

>



Y para los forwards de error y acceso no permitido incluimos lo siguiente a continuación:

>
name="error" path="/error.jsp" contextRelative="true" />
name="permisoDenegado" path="/permisoDenegado.jsp" contextRelative="true" />
>



Por último tenemos que crear las páginas error.jsp y permisoDenegado.jsp

Bueno, pues hemos llegado al final...
Volver a actualidad

Etiquetas: j2ee, Struts, perfiles, permisos, Seguridad, acceso

Comentarios: 13

  • venkman 14/05/2008 09:58

    Dos cosas:

    1. No seas tan modesto: Pon tu nombre en algún sitio :)

    2. Después de darle bastantes vueltas al asunto yo he llegado a la conclusión de que no existe realmente necesidad de diferenciar entre "área de acceso" y "tipo de operación". Es posible simplificar si plantea como "operación".

    Es decir, donde tú tendrías, por poner un ejemplo, las áreas de socios y de cursos, y los tipos de operaciones de leer, alta, baja y modificación, se podría tener viendolo de otro modo, las operaciones "ver socios, alta de socio, baja de socio, alta de curso, modificar curso, etc".

    ¿Por qué -qué ventaja tiene, si es que tiene alguna- plantearlo así? ¿No es una especie de desnormalización?

    Puede verse como una especie de desnormalización, pero es que en realidad, el concepto de "área de acceso" está íntimamente ligado al de las operaciones. Quiero decir, conceptualmente existe una relación de dependencia bastante fuerte que, al separar ambos, da lugar a determinadas situaciones que no aportan nada. Por ejemplo, ¿qué sentido tiene tener acceso a un área si no se tiene permiso para ningún tipo de operación en ella? Ninguno. En realidad, lo que ocurre es que el propio acceso al área ya es un tipo de operación, o mejor dicho una operación en sí misma.

     

    Claro... esto tiene una pega. El sistema de tipos de operaciones facilita bastante la asignación de permisos masivamente (masivamente en la dimensión de las operaciones, no en la de los usuarios). El sistema de operaciones hace algo más sencilla la asignación granulada de permisos pero no ayuda particularmente a asignar, por ejemplo, "todos los permisos de lectura". (Se puede resolver mediante algún tipo de metadato o con plantillas, pero es menos limpio y elegante, lo reconozco) (También hay que tener en cuenta que mientras que la comprobación de permisos se va a realizar en toda la aplicación, este problema sólo afecta al momento de definir roles y asignarles los permisos, una operación de administración)

  • Anónimo 14/05/2008 10:05

    Si alguien me puediera explicar lo siguiente ..... 

    No sé cómo hacerlo, no entiendo muy bien la parte de:

     "A partir de ahora, el parámetro de tipo ActionMapping del método execute de los Action será de tipo SecurityActionMapping (podremos hacer un casting dentro del método a un objeto de esta clase con el objeto de entrada). ..."

     Muchas gracias

  • Anónimo 14/05/2008 13:05

    Al inicio del Action tuyo, tienes que poner:

             SecurityActionMapping securityMapping = (SecurityActionMapping)mapping;

  • Anónimo 14/05/2008 13:08

    A mi me da un problema cuando intento levantar el servidor:

    java.lang.NoSuchMethodException: Bean has no property named seccionAplicacion

    pero tengo el Bean y el struts-config.xml perfecto.

     Si alguien me puede ayudar...

    Muchas gracias

  • Anónimo 14/05/2008 13:13

    Hola,

     Y no sería más sencillito utilizar un Filtro y nombrar adecuadamente los actions? Así, todos los acabados en *Admin pues se exigiría el rol de administrador, *User de usuario, ... esto convinado con JAAS creo q simplifica bastante el tema.

    Si utilizar Spring el módulo Acegi es la caña.

    Saludos

     

  • Anónimo 14/05/2008 13:21

    Se deberia de controlar el permiso cada vez que cambias de pagina (de action).

    Haciendolo con un filtro solo lo comprobarias una vez al entrar a la aplicacion no?

    Puedes concretar un poco?

    Gracias

  • venkman 14/05/2008 13:59

    "Haciendolo con un filtro solo lo comprobarias una vez al entrar a la aplicacion no?"

    No sé de dónde has sacado esa idea, pero no. Los filtros se ejecutan en cada petición. Por eso se llaman filtros.

  • Anónimo 14/05/2008 16:05

    Otra solucion es usar un Controller.

  • Anónimo 14/05/2008 16:30

    He resuelto el problema que me daba a mi.

    Tenia puesto en el struts-config.xml, dentro de cada tag , la palabra "classname" en lugar de "className"

  • greeneyed 14/05/2008 22:35

    Yo lo que uso y definimos hace bastante es una serie de interfaces a implementar por cada aplicacion, que:

    .- Te devuelvan si un usuario/clave es correcto
    .- Te devuelvan si un usuario tiene permiso para realizar una accion (rol)
    .- Te devuelvan la accion(rol) requerido para cada peticion (pudiendo decidirlo en funcion de parametros, el contexto, la fase lunar... :) )

    Implementando esas interfaces, tenemos aplicaciones que tienen un par de usuarios en ficheros de tecto y solo tienen permisos simples, aplicaciones con usuarios en BDD y permisos que cambian dinamicamente en funcion de los parametros de la peticion... y algunos que comprueban datos del contexto, hora/otros parametros, para decidir si tiene o no permisos el usuario para eso.

    Todo independiente de la logica de la aplicacion y sin recompilar ninguna clase de logica para protegerla o no, pudiendo reutilizar la misma clase para operaciones protegidas o no.

    Debemos estar contentos por que hace años que lo usamos :D

  • Anónimo 15/05/2008 00:04

    Definitivamente me quedo con Acegi (Spring security), es independiente de struts y no es intrusivo, que feo se mira (diseño) eso de extends RestrictionAction, SecurityActionMapping, AccesoNoPermitidoException, yo he logrado integrar Acegi con struts1 y struts2 y es completemente flexible y te ofrece las mejores prácticas para seguridad de aplicaciones JEE, no hay que inventar mucho, así que muy bueno el tutorial para aprender a extender y personalizar struts, pero muy obsoleto la aplicabilidad.

  • Anónimo 16/05/2008 08:32

    Yo creo que struts ya trae integrado un modulo para conectarse con los roles de JAAS que defines en el web.xml, los famosos contrains.

    El filtro funciona bastante bien para darse cuenta si el usuario se encuentra logueado o cargar los permisos del mismo.

    Por otro lado se puede hacer un taglib para determinar si se debe o no mostrar o permitir la edicion de los elementos de la pagina.

    Tengo en cola probar Acegi a ver que tal.

    En cuanto a la solucion, pues si ya la habia elegido, pero el problema que siento en utilizar jerarquias de actions, es que te amarras demasiado a un controller especifico, pues por ejemplo si quisieras pasar paulatinamente a SpringMVC, se te va ser mas complicado, pues tambien tiene que migrar el codigo base, en vez de solo la logica de control del action.

    En fin, es mi humilde opinion,

    salu2

    J

  • Anónimo 20/05/2008 21:25

    hay manuales de jsp estoy ingresando en l tema por favor gracias

     

Escribe tu comentario

Sun Microsystem Logo NHT-Norwick Logo

© 2002-2007 Asociación javaHispano