viernes, 12 de julio de 2013

Pasar datos desde JSP hacia SiteMesh decorators


Buscando la forma de pasar parametros y contenido entre JSP y los decoradores de SiteMesh encontre lo siguiente aquí

Básicamente consta en crear contenido en el JSP y ponerlo dentro dle tag <content>.

<content tag="pageName">
    Login Page
</content>
<content tag="siteSection">
    Administration
</content

arriba se declarna dos contenidos, luego en el JSP del decorador la info se extrae mediante

<decorator:getProperty property="page.pageName"/>
<decorator:getProperty property="page.siteSection"/>

eso hará que al aplicar el decorador, traiga todo lo que contenga el tag <contentent> identificado por pageName y siteSection.


miércoles, 10 de julio de 2013

Test Dev

// Comment
public class Testing {
public Testing() {
}
 
public void Method() {
/* Another Comment
on multiple lines */
int x = 9;
}
}

Tutorial primeros pasos con Netbeans + Spring MVC 3 + Spring Security 3 + SiteMesh


El siguiente tutorial tiene como objetivo desarrollar un proyecto básico paso a paso utilizando el IDE Netbeans 7.3, el Framework de Spring MVC 3, Spring Security 3 y SiteMesh 2.4.2


Herramientas y librerías



  • Netbeans 7.3
  • Spring MVC 3 (agregado desde Netbeans)
  • Spring Security 3.1.4  Bajado Desde acá
  • JAR asm-all-3.1.2.jar
  • JAR cglib-2.2.jar
  • SiteMesh 2.4.2 Bajado desde acá
  • JAR com.springsource.org.aopalliance-1.0.0.jar
  • Ejemplo en Netbeans bajar de acá  <-- no olviden bajar Spring Framework Security y agregarlo en librerias. 
  • Ejemplo en GitHub bajar de aqui



1- Crear Proyecto en NetBeans. 



Primero seleccionamos nuevo proyecto desde el menú principal de Netbeans, buscamos la categoría Java Web y seleccionamos el project "Web Application" 


En los siguientes pasos se elige el servidor embebido para testear el proyecto, en este caso probaremos con GlasshFish Server 3.1.2. De forma adicional se puede setear el "Context Path" (URL del proyecto), si es que no les gusta el que tira por defecto el IDE (en base al nombre del proyecto).

Luego le damos siguiente.


En este paso se eligen los frameworks a utilizar, por lo cual marcamos la casilla de "Spring Web MVC", incluimos los JSTL y la versión del Spring Framework 3.1.1 RELEASE, esto hará que el IDE automáticamente incluya dentro de las librerías del proyecto los componentes de Spring MVC 3.


La estructura final del proyecto al ser creado se ve la siguiente forma:




Las librerías del proyecto quedan así: 























Si echamos a correr el proyecto (RUN) debería verse lo siguiente: 





2- Agregar Librerías adicionales. 




Agregamos las librerías de Spring Scurity 3, SiteMesh y el resto declaradas anteriormente. Esto lo podemos hacer vía propiedades del proyecto.



El resultado debiese ser de la siguiente forma:



por un tema practico simplemente agregue todas los JAR de Spring Security.

3- Configuración SiteMesh 


Ahora pasaremos a generar la configuración y archivos básicos para utilizar SiteMesh.

Primero creamos la carpeta "decorators" dentro de WEB-INF. (puede ser en otra ruta). Dentro de decorators crearemos nuestro primer decorador "basic-theme.jsp" de momento no pondremos código adicional al JSP.

La estructura se debería ver así:
























Ahora debemos modificar el web.xml de nuestro proyecto para agregar el filtro correspondiente a SiteMesh. De manera que las solicitudes hacia nuestra aplicación sean filtradas por SiteMesh y decoradas según sea el caso.

nota: la primera linea de los .xml debe partir con "<?xml version="1.0" encoding="UTF-8"?>" si hay un espacio antes, al tratar de correr la aplicacion obtendrán un error de parseo.

Web.xml: 
  

    
     sitemesh
     
     com.opensymphony.sitemesh.webapp.SiteMeshFilter
     
    

    
     sitemesh
     /*
 FORWARD
 REQUEST     
         


    
        contextConfigLocation
        /WEB-INF/applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        2
    
    
        dispatcher
        *.htm
    
    
        
            30
        
    
    
        redirect.jsp
    


Ahora debemos crear un archivo "decorator.xml" dentro de /WEB-INF/




Dentro de decorator.xml ponemos lo siguiente:


    
    
        /exclude.jsp
        /exclude/*
    

    
        /*
    


En el tag decorators indicamos cual sera el directorio por defecto para nuestros decoradores, en este caso seteamos "defaultdir="/WEB-INF/decorators".  Por esta razon el decorador base "basic-theme.jsp" fue creado dentro de WEB-INF/decorators, esta es una variable que debe ser seteada según la regla que se defina para el proyecto.

Además dentro de este xml de definición, indicamos los path a excluir, es decir, archivos que no deben ser decorados por sitemesh, por ejemplo estilos CSS, Javascript, Ajax, etc... Los path hacia esos archivos deben ser agregados dentro de las exclusiones.

Este xml también permite definir patrones, ne este caso toda URL que cumpla con "/*" será decorada.

Ahora volvamos a basic-theme.jsp en donde incluimos el siguiente código:


<?xml version="1.0" encoding="UTF-8" ?>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <decorator:head />
</head>
<body>
    <h1>Header</h1>
    <p><b>Navigation</b></p>
    <hr />
    <decorator:body />
    <hr />
    <h1>Footer</b></h1>
</body>
</html>

En este decorador, se indica a SiteMesh mediando TAGs que debe tomar los que encuentre dentro del TAG <head> y <body> de las paginas a decorar, y ponerlo (incrustarlo) dentro de basic-theme.jsp. En otras palabra sitemesh extrae objetos desde otras paginas y las redenderiza con el tema o layout especificado.

Si le damos RUN a la aplicacion deberiamos ver lo siguiente:


Como se aprecia en la imagen, el contenido de index.jsp, visto al principio de este tutorial, fue "decorado" en base a lo que se definió en el decorador basic-theme.jsp.


4- Spring Security 3 y MVC 3


Ahora nos dedicaremos a configurar los XMLs necesario para dotar de la capa de seguridad a nuestro proyecto, para esto primero debemos crear un XML en donde se definan los parametros y reglas de seguridad, lo llamaremos spring-security.xml y lo crearemos bajo /WEB-INF/  junto a los otros xml de configuración

spring-security.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:security="http://www.springframework.org/schema/security"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
   http://www.springframework.org/schema/security 
   http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <!-- enable support for Expression-based annotations in Spring Security secured-annotations="enabled"  -->
    <security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled" />
    
    <!--
    <security:http auto-config="true" use-expressions="true" access-denied-page="/accessdenied" >
        
        <security:form-login />
 
        <security:logout />
 
    </security:http>
    -->
    
     <security:http auto-config="true" use-expressions="true" access-denied-page="/accessdenied">
  <security:intercept-url pattern="/welcome*" />
  <security:form-login login-page="/login" default-target-url="/welcome"
   authentication-failure-url="/loginfailed" />
  <security:logout logout-success-url="/logout" />
 </security:http>
   
    <!-- Declare an authentication-manager to use a custom userDetailsService -->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userDetailsService">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>
 
    <!-- Use a Md5 encoder since the user's passwords are stored as Md5 in the database -->
    <bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>
  <!-- An in-memory list of users. No need to access an external database layer.
      See Spring Security 3.1 Reference 5.2.1 In-Memory Authentication -->
  <!-- john's password is admin, while jane;s password is user  -->
    <security:user-service id="userDetailsService">
        <security:user name="admin" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_USER, ROLE_ADMIN" />
        <security:user name="user" password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER" />
    </security:user-service>
    
    
</beans>

Notar que los schemlocation apunta a la version 3.1 de spring security, en muchos casos los ejemplos que vienen dentro de la distribución vienen con XML apuntando a versiones anteriores.

Dentro de este XML se define la pagina de login, la página de acceso denegado y la pagina de exito en logout o en caso de error.

Ahora debemos modificar el archivo "applicationContext.xml", este archivo es generado automaticamente por Netbeans, en principio se ve asi:

applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

    <!--bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
          p:location="/WEB-INF/jdbc.properties" />

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="${jdbc.driverClassName}"
    p:url="${jdbc.url}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}" /</script>

    <!-- ADD PERSISTENCE SUPPORT HERE (jpa, hibernate, etc) -->
</beans>

y debemos reemplazarlo por lo siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.1.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config/>
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans. For example @Controller and @Service. Make sure to set the correct base-package-->
<context:component-scan base-package="com.ozdev"/>
<!-- Configures the annotation-driven Spring MVC Controller programming model. Note that, with Spring 3.0, this tag works in Servlet MVC only! -->
<mvc:annotation-driven/>
</beans>

Aquí hemos indicado que se habilitan las anotaciones en el framework y la ruta al package base que contendrá nuestras clases con anotaciones.
en construcción.

De manera análoga generamos el siguiente contenido para dispatcher-servlet.xml

dispatcher-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.1.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
    
    
    <bean id="jspViewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass"
                    value="org.springframework.web.servlet.view.JstlView" />
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
    </bean>
    
</beans>

Ahora debemos indicarle a nuestro web.xml que debe utilizar el filtro de spring-security para eso debemos añadir los correspondientes filtros en web.xml y además como parámetro de contexto agregamos a spring-security.xml. El archivo web.xml final debiese quedar asi:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
         xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>  

    <filter>
     <filter-name>sitemesh</filter-name>
     <filter-class>
     com.opensymphony.sitemesh.webapp.SiteMeshFilter
     </filter-class>
    </filter>

    <filter-mapping>
     <filter-name>sitemesh</filter-name>
     <url-pattern>/*</url-pattern>
 <dispatcher>FORWARD</dispatcher>
 <dispatcher>REQUEST</dispatcher>     
    </filter-mapping>     


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>redirect.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Ahora creamos nuestro primer controlador, en este caso nos interesa que se maneje el login de acceso, primero crearemos la clase JAVA LoginController, luego generaremos los JSP faltantes (login y accessdenied).

LoginController.java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ozdev.controllers;

/**
 *
 * @author ozdev
 */
import java.security.Principal;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
public class LoginController {
 @Secured("ROLE_ADMIN")
 @RequestMapping(value="/welcome", method = RequestMethod.GET)
 public String printWelcome(ModelMap model, Principal principal ) {
 
  String name = principal.getName();
  model.addAttribute("username", name);
  model.addAttribute("message", "Spring Security Custom Form example");
  return "hello";
 
 }
 
 @RequestMapping(value="/login", method = RequestMethod.GET)
 public String login(ModelMap model) {
 
  return "login";
 
 }
        
 @RequestMapping(value="/", method = RequestMethod.GET)
 public String loginRaiz(ModelMap model) {
 
  return "login";
 
 } 
        
 
 @RequestMapping(value="/loginfailed", method = RequestMethod.GET)
 public String loginerror(ModelMap model) {
 
  model.addAttribute("error", "true");
  return "login";
 
 }
 
 @RequestMapping(value="/logout", method = RequestMethod.GET)
 public String logout(ModelMap model) {
 
  return "login";               
 }
        
                
  @RequestMapping(value="/accessdenied", method = RequestMethod.GET)
 public String accessdenied(ModelMap model) {
 
  return "accessdenied";        
        }
 
}

En las notaciones de spring framework utilizamos el @RequestMapping para indicar la URL que nos interesa interceptar y en el return indicamos la página (JSP) la cual será la vista.

Por el lado de la seguridad indicamos que la pagina /welcome solo tendra acceso el rol admin con lo siguiete:  @Secured("ROLE_ADMIN") .

Ahora generamos los JSP faltantes

accessdenied.jsp:

<%-- 
    Document   : accessdenied
    Created on : Jul 27, 2012, 11:50:54 AM
    Author     : ozdev
--%>

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        Access is denied
    </body>
</html>

login.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.errorblock {
 color: #ff0000;
 background-color: #ffEEEE;
 border: 3px solid #ff0000;
 padding: 8px;
 margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
 <h3>Login with Username and Password (Custom Page)</h3>
 
 <c:if test="${not empty error}">
  <div class="errorblock">
   Your login attempt was not successful, try again.<br /> Caused :
   ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
  e</div>
 </c:if>
 
 <form name='f' action="<c:url value='${pageContext.request.contextPath}/j_spring_security_check' />"
  method='POST'>
 
  <table>
   <tr>
    <td>User:</td>
    <td><input type='text' name='j_username' value=''>
    </td>
   </tr>
   <tr>
    <td>Password:</td>
    <td><input type='password' name='j_password' />
    </td>
   </tr>
   <tr>
    <td colspan='2'><input name="submit" type="submit"
     value="submit" />
    </td>
   </tr>
   <tr>
    <td colspan='2'><input name="reset" type="reset" />
    </td>
   </tr>
  </table>
 
 </form>
</body>
</html>

hello.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
 <h3>Message : ${message}</h3> 
 <h3>Username : ${username}</h3> 
 
 <a href="<c:url value="/j_spring_security_logout" />" > Logout</a>
 
</body>
</html>

Si le damos RUN al proyecto entraremos a la siguiente página:



si nos logeamos como user: admin pass: admin veremos esto:



en cambio si entramos como user/user veremos esto:


Con esto concluimos el levantamiento de un proyecto con Spring MVC 3, SiteMesh y Spring Security 3