seguridad jsf _ gishac's blog

15
TAG ARCHIVES: SEGURIDAD JSF Java Series – Autenticación y Autorización en J2EE con Glassfish Posted on March 24, 2012 En el 2011 escribí un post muy parecido a éste, sobre cómo manejar autenticación y autorización a través filtros; ahora vamos a mejorar la situación y vamos a manejar la autenticación a través de jdbcRealms con el servidor web glassfish, aunque el esquema puede ser utilizado en otros servidores web, y la autorización con security-constraints en el web.xml El objetivo? pues lo mismo de siempre; proteger los recursos web a través de restricciones de seguridad; viendolo a gran escala: Se tienen las tablas de rol, usuario y roles por usuario en una base de datos relacional (a los roles se los conoce como grupos en éste ámbito) Se define en el archivo web.xml los recursos protegidos (security-constraint) y se indica que roles tienen acceso a estos recursos (authorization-constraint), los roles que se definen aquí no necesariamente deben coincidir con el nombre del grupo en la base de datos. En el archivo sun-web.xml se hace el mapeo entre rol definido en el archivo web.xml y el grupo (rol) en la base de datos Se crea en el servidor web un Security Realm donde configuramos la base de datos, y las tablas que contienen los datos de usuario y rol Creamos un formulario web, e invocamos al metodo login de la clase HttpServletRequest que por debajo intenta autenticarse contra las tablas de usuario y rol configuradas en el realm. El modelo entidad relación que espera la configuración es el siguiente:

Upload: alfonso-enrique-puerta-julio

Post on 02-Dec-2015

261 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Seguridad Jsf _ Gishac's Blog

T AG ARCHIVES: SEGURIDAD JSF

Java Series – Autenticación y

Autorización en J2EE

con GlassfishPosted on March 24, 2012

En el 2011 escribí un post muy parecido a éste, sobre cómo manejar autenticación y

autorización a través filtros; ahora vamos a mejorar la situación y vamos a manejar la

autenticación a través de jdbcRealms con el servidor web glassfish, aunque el esquema

puede ser utilizado en otros servidores web, y la autorización con security-constraints en el

web.xml

El objetivo? pues lo mismo de siempre; proteger los recursos web a través de restricciones

de seguridad; viendolo a gran escala:

Se tienen las tablas de rol, usuario y roles por usuario en una base de datos relacional

(a los roles se los conoce como grupos en éste ámbito)

Se define en el archivo web.xml los recursos protegidos (security-constraint) y se indica

que roles tienen acceso a estos recursos (authorization-constraint), los roles que se

definen aquí no necesariamente deben coincidir con el nombre del grupo en la base de

datos.

En el archivo sun-web.xml se hace el mapeo entre rol definido en el archivo web.xml y

el grupo (rol) en la base de datos

Se crea en el servidor web un Security Realm donde configuramos la base de datos, y

las tablas que contienen los datos de usuario y rol

Creamos un formulario web, e invocamos al metodo login de la

clase HttpServletRequest que por debajo intenta autenticarse contra las tablas de

usuario y rol configuradas en el realm.

El modelo entidad relación que espera la configuración es el siguiente:

Page 2: Seguridad Jsf _ Gishac's Blog

Pero lo mas común es tener un esquema así:

Para hacerlo transparente simplemente hacemos una vista de las tablas de la siguiente

manera:

Al hacer un select sobre la vista debe devolver los usuarios con sus roles asignados:

Ahora vamos a configurar el glassfish; lo primero es considerar tener el driver jdbc en el

classpath del servidor, lo recomendable es colocarlos en la carpeta:

[ruta_instalación_glassfish]/glassfish/domains/tu_domain/lib/ext

Ahora accedemos a la consola de administración, que por defecto se accede

por http://localhost:4848 , comenzamos creando un pool de conexiones, dentro de

1234

SELECT dbo.[user].username, dbo.[user].password, dbo.role.roleFROM dbo.[user] INNER JOIN dbo.user_role ON dbo.user_role.username = dbo.[user dbo.role ON dbo.user_role.role_id = dbo.role.id

Page 3: Seguridad Jsf _ Gishac's Blog

Resources-JDBC-JDBC Connection Pools creamos un pool, que consta de 2 pasos:

En el paso 1 le damos un nombre al pool y definimos a que tipo de base de datos se va a

conectar

En el paso 2 colocamos los datos para la conexión:

Una vez creado el pool, comprobamos que todo esta correcto validando con un ping desde la

consola de glassfish:

Page 4: Seguridad Jsf _ Gishac's Blog

Una vez definido el pool creamos un recurso jdbc dentro de Resources-JDBC-JDBC

Resources, simplemente le asignamos un nombre y como pool seleccionamos al pool de

conexiones creado anteriormente:

Finalmente nos falta crear el realm de seguridad dentro de Configurations-server_config-

Security-Realms, lo primero es darle un nombre, el cual vamos a utilizar luego en la

configuración del web-xml de la aplicación web, la configuración va de la siguiente manera:

Page 5: Seguridad Jsf _ Gishac's Blog

Lo que se debe tomar en cuenta: En classname debemos escoger

com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm, en el JAASContext debemos

colocar jdbcRealm, en el JNDI colocamos el nombre del JDBCResource creado

anteriormente, en los campos User Table y Group Table colocamos el nombre de la vista que

hicimos de las tablas de autenticación, así mismo con los nombres de los campos de user,

password y group (rol); y los datos de user y password para la conexión a la base de datos.

Ahora creamos un proyecto web en eclipse configurado para hacer deployment en glassfish,

la estructura del sitio consta de la sección para administradores (admin) y la sección para

editores (editor), en el web.xml debemos configurar todo lo referente a autenticación y

autorización:

12345678

<login-config> <auth-method>FORM</auth-method> <realm-name>demoSecurityRealm</realm-name> <form-login-config> <form-login-page>/login.jspx?faces-redirect=true</form-login-page> <form-error-page>/loginerror.jspx</form-error-page> </form-login-config> </login-config>

Page 6: Seguridad Jsf _ Gishac's Blog

El detalle de las configuraciones:

El modo de autenticación configurado es FORM, el realm-name es el realm que

creamos dentro de glassfish

Creamos 2 tags de security-role, estos roles pueden ser o no los mismos nombres que

los que se encuentran en la base de datos, luego vemos donde realizamos el mapeo,

por ahora están definidos 2 roles: webAdmin y webEditor

Creamos 2 tags de security-constraint para las areas de administradores y editores, en

el auth-constraint colocamos los nombres de los roles definidos en los tags de security-

role, respectivamente para cada sección

Finalmente en el archivo sun-web.xml debemos mapear los nombres de los roles definidos en

el archivos web.xml con los roles de la base de datos:

910111213141516171819202122232425262728293031323334

<security-role> <role-name>webAdmin</role-name> </security-role> <security-role> <role-name>webEditor</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>admin-area</web-resource-name> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <description>admins should be allowed to access this resources</description <role-name>webAdmin</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>editor-area</web-resource-name> <url-pattern>/editor/*</url-pattern> </web-resource-collection> <auth-constraint> <description>editors should be allowed to access this resources</description <role-name>webEditor</role-name> </auth-constraint> </security-constraint>

12345678910111213141516

<sun-web-app error-url=""> <context-root>/J2EESecurityRealm</context-root> <security-role-mapping> <role-name>webAdmin</role-name> <group-name>Administrator</group-name> </security-role-mapping> <security-role-mapping> <role-name>webEditor</role-name> <group-name>Editor</group-name> </security-role-mapping> <class-loader delegate="true" /> <jsp-config> <property name="keepgenerated" value="true"> <description>Keep a copy of the generated servlet class java code.</ </property> </jsp-config>

Page 7: Seguridad Jsf _ Gishac's Blog

El sun-web.xml es muy sensible incluso en el orden de los tags, así que tomenlo en cuenta.

Finalmente queda hacer la página para autenticarnos y probar que funciona todo lo realizado

previamente.

Detras tenemos un managed bean que se va a encargar de autenticarnos:

17 </sun-web-app>

1234567891011121314151617181920

<h:body style="height: 100%;"> <f:view> <h:form id="form"> <h:panelGrid columns="2"> <h:outputLabel id="user">User:</h:outputLabel> <h:inputText value="#{loginBean.username}"></h:inputText <h:outputLabel id="password">Password:</h:outputLabel> <h:inputSecret value="#{loginBean.password}"></h:inputSecret <h:commandButton action="#{loginBean.login}" value="Login" </h:panelGrid> <h:messages id="errorMessages"></h:messages> <h:panelGrid rendered="#{request.getUserPrincipal() != null}" <a href="admin/adminIndex.jspx">Admin Zone</a> <a href="editor/editorIndex.jspx">Editor Zone</a> </h:panelGrid> </h:form> </f:view></h:body>

123456789101112131415161718192021222324252627282930

</pre>@ManagedBean(name="loginBean")@RequestScopedpublic class LoginBean { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public void login(){ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); try { request.login(username, password); } catch (Exception e) { FacesContext.getCurrentInstance().addMessage("", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid credentials, try again" } }

Page 8: Seguridad Jsf _ Gishac's Blog

El método principal del bean es el login donde como vemos de manera transparente

ejecutamos request.login y esto automagicamente nos trata de autenticar utilizando el

security realm definido en el glassfish, si tratamos de acceder a las secciones de admin o

editor nos hace una especie de redirect a la pagina de login, pero como vemos mantiene la

url:

Si nos autenticamos con credenciales correctas la página de login nos presenta el menú para

acceder a las distintas secciones, ya que estamos utilizando la propiedad rendered del panel

para indicarle cuando mostrarse:

Si el usuario con el que nos autenticamos tiene un rol que tiene acceso a las secciones

3132333435363738394041424344

public void logout() throws ServletException, IOException{ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); request.getSession().invalidate(); request.logout(); FacesContext.getCurrentInstance().getExternalContext().redirect( } public String getLoggedUser(){ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); if(request.getUserPrincipal() != null) return request.getUserPrincipal().getName(); return ""; }}

Page 9: Seguridad Jsf _ Gishac's Blog

definidas con los security-constraints internamente el servidor le dara acceso sin problemas,

automáticamente hace la validacion entre los roles asignados en la base de datos con la

autorización que tienen los mismos en el web.xml.

En este caso el usuario gishac tiene ambos roles, si se autentican con un usuario que tiene

un rol que no tiene acceso a la sección el servidor le niega el acceso al recurso, para el

ejemplo me autentico con el usuario editor que solo tiene el rol Editor.

En asp.net hay una característica que la maneja de modo automática la plataforma, cuando

un usuario que no esta autenticado trata de acceder a una zona protegida y es enviado a la

página de login y se autentica, automáticamente es redirigido a la página que le fue negado el

acceso, en jsf hay que usar un pequeno artificio, el cual consiste en crear en el bean una

Page 10: Seguridad Jsf _ Gishac's Blog

propiedad que contiene el valor de la página a redirigir, y en el metodo login validamos si tiene

algun valor para redirigirlo; finalmente el código del bean queda así:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758

@ManagedBean(name="loginBean")@RequestScopedpublic class LoginBean { private String username; private String password; @ManagedProperty(value="#{param.from}") private String from; public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public void login(){ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); try { request.login(username, password); if(from != null){ FacesContext.getCurrentInstance().getExternalContext().redirect(from); } } catch (Exception e) { FacesContext.getCurrentInstance().addMessage("", new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid credentials, try again" } } public void logout() throws ServletException, IOException{ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); request.getSession().invalidate(); request.logout(); FacesContext.getCurrentInstance().getExternalContext().redirect( } public String getLoggedUser(){ HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); if(request.getUserPrincipal() != null) return request.getUserPrincipal().getName(); return ""; }}

Page 11: Seguridad Jsf _ Gishac's Blog

Y en el xhtml de la página de login colocamos un input hidden:

El ejemplo puede ser portado a otro servidor web (servlet container, etc etc), por ejemplo

tomcat, sólo tienen que crear el jdbcRealm en el mismo.

Saludos,

gish@c

Descargar fuentes

Posted in J2EE, java, java series, jsf, security, software development, web security |

Tagged autenticacion, authentication, authorization, autorizacion jsf, glassfish, java

security, login jsf, seguridad java, seguridad jsf | 4 Replies

1 <input type="hidden" name="from" value="#{requestScope['javax.servlet.forward.request_uri']}"

Java Series – Authentication y

Authorization en J2EEPosted on February 28, 2011

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:

Page 12: Seguridad Jsf _ Gishac's Blog

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:

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

12345

public void init(FilterConfig filterConfig); public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain); public void destroy();

Page 13: Seguridad Jsf _ Gishac's Blog

siguiente configuración:

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:

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:

12345678

<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>

123456789101112131415161718192021222324

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;} }

123

<filter> <filter-name>AuthSuperAdminFilter</filter-name> <filter-class>demo.security.AuthSuperAdminFilter</filter-class>

Page 14: Seguridad Jsf _ Gishac's Blog

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

Filtro para Sección de Super Administradores

45678

</filter><filter-mapping> <filter-name>AuthSuperAdminFilter</filter-name> <url-pattern>/superadmin/*</url-pattern></filter-mapping>

123456789101112131415161718192021222324

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) 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; }

12345678910111213141516171819202122

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) 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

Page 15: Seguridad Jsf _ Gishac's Blog

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

Posted in desarrollo, J2EE, java, java series, jsf, netbeans, security, software

development, web security, web2.0 | Tagged autenticacion, authentication,

authorization, autorizacion jsf, j2ee, java security, jsf, jsf filter, jsf2, jsf2

authentication, jsf2 authorization, security, seguridad, seguridad j2EE, seguridad

java, seguridad jsf | 2 Replies

2324252627

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