Spring Security + OAuth2

Saludos mi nombre es Wilman de Colombia, el propósito de escribir este blog es mantener un recordatorio de todas aquellas cosas que voy aprendiendo en este camino del desarrollo, esta es la primera vez que escribo un blog y espero que alguien le pueda servir de algo.

En esta primera entrada voy a tratar de abarcar muchos temas relacionados con el desarrollo web, movil y algo de la Soa Suite de Oracle. El objetivo de poder ver como se pueden integrar muchas de las tecnologías que existen actualmente  para construir una aplicación robusta, mas o menos esta sería la arquitectura de nuestra aplicación:

 

Arquitectura de la aplicación

En esta mi primera entrada nos vamos a enfocar el desarrollo del servidor de autenticación y el servidor de recursos, he de decir que soy muy curioso y en uno de esos días de navegar y navegar me encontré con la especificación de este framework oauth2, después de leerlo un poco me pareció interesante así que comencé a mirar que implementaciones estaban disponibles para java, y me encontré con que Spring Framework tenía una y como soy un desarrollador pro-spring me vino como anillo al dedo. Así que despues de mucho leer y googlear comencé con la implementación de spring security junto con otros frameworks como hibernate, log4j, aspectj, maven, etc…, otras de las cosas que noté es que todo actualmente en el mundo java está tendiendo a Anotaciones y configuración desde java en el ejemplo que les ilustraré a continuación decidí comenzar a adaptarme a este enfoque.

ACTUALIZACIÓN 1(22-09-2016): después de mucho investigar sobre los servicios rest me encontré varios blogs acerca de las buenas prácticas al momento de construir nuestra api rest, por este motivo he decidido aplicar las buenas practicas que encontré, con el propósito de que aquellas personas que empiezan a aprender sobre este mundo RESTFul lo hagan desde un inicio con las buenas prácticas, revisando el código me di cuenta que no cumplía con las buenas prácticas, las buenas prácticas que noté que tenían en común los blogs que consulté las listo a continuación:

1.- SIEMPRE garantizar sobre SSL nuestros servicios ya que mucha de la información viaja plana por internet y cualquiera puede interceptarla.

2.- Usar siempre NOMBRES en vez de VERBOS por ejemplo en mi código tenía cosas como “crudClientes/infoCliente” o cosas como “/crearProducto” que sustituí por “http://…/clientes/123456?…”

3.- Usar los nombres en plural por ejemplo “/clientes, /productos, /facturas, /documentos

4.- Usar autenticación basada en tokens como lo estamos haciendo

5.- Usar json sobre xml

6.- Versionar nuestra API, ya que nuestra api puede crecer y esto nos permitirá tener un mayor control sobre ese crecimiento precisamente, en mi código no tenía versiones en las clases anotadas con @RestController tenía los siguiente “@RequestMapping(value = “/rest”)” y lo cambie por “@RequestMapping(value = “/api/v1.0”)

7.- Usar los códigos http correctamente  acá pueden verlos , en mi caso estaba usando códigos personalizados y mensajes encapsulados viendo uno de los blog encontré que Spring tiene una clase o un Enum con los códigos http esta clase es HttpStatus

Incorrecto -> 

       InfoResponse infoResponse = new InfoResponse();
       infoResponse.setCode("000");
       infoResponse.setDescription("Services execute succefully");

       response.setHeader(infoResponse);
       response.setMessage("Hola. " + ip.getNombre() + ", mucho gusto \n " + new             Date());

Correcto ->

     return new ResponseEntity<String>(sb.toString(), HttpStatus.OK);

Como se puede observar Spring tiene la clase ResponseEntity con varios constructores que nos ayudaran a usar tanto los códigos Http como envíar la información que necesitemos en nuestro pequeño ejemplo retorna un String, aparte de que el código queda mas limpio y legible.

Para cumplir con esta buena práctica eliminé el paquete co.com.microservice.application.servicesaux.

Bueno estas son algunas pero pueden profundizar en este artículo  que está mas completo.

Agradecimiento especial para la persona que escribió este blog me ayudó mucho a comprender lo que estaba haciendo mal y como hacerlo mejor.

ACTUALIZACIÓN 2 (27-09-2016): Investigando encontré que en algunas ocasiones no es bueno devolver todos los registros de un solo envión porque se puede hacer uso de la paginación y existen 2 formas de hacerlo para los servicios REST una como un recurso o como una representación en estos blogs lo explican mejor Rest Service  y en este otro Paginación.

También agregue algunos ejemplo de como usar el comando curl, personalmente no lo había utilizado los coloqué en este blog para tenerlo de recuerdo.

ACTUALIZACIÓN 3(28-09-2016): Ajusté algunos métodos  de las clases DaoImpl para eliminar los @SuppressWarnings(“unchecked”) que se generaban por el Type Safe: Uncheck cast.

ACTUALIZACIÓN 4(30-09-2016): En esta actualización logré después de googlear mucho poder registrar la información del cliente(Spring Security) en la base de datos y de esta manera no tenerlo harcodeado sino que ahora podemos registrar nuestro cliente en una base de datos y hacer la validación de manera dinámica, para ello se agregaron varías tablas al esquema entre ellas OAUTH_CLIENT_DETAILS  se creó la entidad correspondiente OauthClientDetails sus respectivos DAOs y sus implementaciones , así como en la capa de servicios, aquí precisamente creé una clase Custom que implementa ClientDetailsService específicamente el método loadClientByClientId, la clase se llama CustomClientDetailService, con toda esta implementación reempazamos lo siguiente:

clients
       .inMemory()
       .withClient("clientapp")
       .authorizedGrantTypes("password", "refresh_token", "client_credentials")
       .authorities("USER")
       .scopes("read", "write")
       .resourceIds(RESOURCE_ID)
       .secret("8649168")
       .accessTokenValiditySeconds(300)
       .refreshTokenValiditySeconds(600)
       .autoApprove(false);

por esto:

   @Autowired
   @Qualifier("customClientDetailsService")
   private ClientDetailsService clientDetailService;
...
   // Con los datos del cliente en base de datos
   clients.withClientDetails(clientDetailService);

Y en la base de datos agregamos el siguiente registro en la tabla OAUTH_CLIENT_DETAILS  :


INSERT INTO OAUTH_CLIENT_DETAILS (CLIENT_ID,
                                  RESOURCE_IDS,
                                  CLIENT_SECRET,
                                  SCOPE,
                                  AUTHORIZED_GRANT_TYPES,
                                  WEB_SERVER_REDIRECT_URI,
                                  AUTHORITIES,
                                  ACCESS_TOKEN_VALIDITY,
                                  REFRESH_TOKEN_VALIDITY,
                                  ADDITIONAL_INFORMATION,
                                  AUTOAPPROVE) 
                            VALUES 
                                  ('clientapp',
                                   'microservice',
                                   '987654321',
                                    'read,write',
                                    'password,refresh_token,client_credentials',
                                    'http://localhost:8086/MicroServices/',
                                    'USER',
                                    '300',
                                    '600',
                                    null,
                                    false);

La ventaja de esto es que podríamos crear nuestras páginas que gestionen nuestros clientes, por ejemplo crear una página de registro y después de registrarlo generar un clientId y un clientSecret automáticos para una aplicación web, android, IOS etc… pero ese será otro blog que tengo pensado hacer precisamente con Spring MVC.

Otra de las cosas que me encontré en el camino mientras investigaba como guardar la información del client en la base de datos era guardar la información del token que se genera, esa información la encontré en este foro decidí probarla y funcionó a la primera para ellos se necesita crear algunas tablas como OAUTH_CLIENT_TOKEN, OAUTH_ACCESS_TOKEN, OAUTH_REFRESH_TOKEN en este caso no usé persistencia para este comportamiento Spring Security ofrece los mecanismos necesarios para realizar las operaciones de guardado, después de crear las tablas lo siguiente es, en nuestra clase AuthorizationServerConfiguration agregar los siguiente:

Eliminamos el almacenamiento del token en memoria:


private TokenStore tokenStore = InMemoryTokenStore()

y editamos los siguientes @Bean:


    @Bean
    public JdbcTokenStore tokenStore() {
        // Antes
        // return new JdbcTokenStore(tokenStore);
        //Despues
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
           DefaultTokenServices tokenServices = new DefaultTokenServices();
           tokenServices.setSupportRefreshToken(true);
           // Antes
           // tokenServices.setTokenStore(this.tokenStore);
           // Despues
           tokenServices.setTokenStore(tokenStore());
           return tokenServices;
    }

En el siguiente método cambiamos el almacenamiento en memoria por el almacenamiento en base de datos:


@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
          endpoints
                .tokenStore(tokenStore())
                .authenticationManager(this.authenticationManager);
}

ACTUALIZACIÓN 5(02-10-2016): En esta actualización edité las versiones de los jar para tener actualizado los frameworks lo mas posible en algunos caso como el de Hibernate noté que en las versiones 5.2.* muchas clases como la org.hibernate.Query han quedado depreciadas, es algo que tengo que estudiar haber porque el cambio, de la misma forma actualicé Spring a la última versión que reportaba la página de maven, spring security tuve un problema con la claseorg.springframework.security.core.authority.SimpleGrantedAuthority al parece tiene un problema con el serialVersionUI, de igual forma agregue las librerías para soporte de AOP tanto de spring como aspectj en sus últimas versiónes, también saqué algo de tiempo y me dediqué a entender el principio HATEOAS(Hipermedia As The Engine Of Applicaction Status) esto quiere decir que tu Api Rest debe tener la capacidad de autodescubrimiento de los recursos que expone a través de un punto de entrada de la aplicación(Si estoy equivocado por favor me corrigen ya que para mí también es un tema nuevo y no lo domino muy bien) pueden profundizar en estos links genbeta, adictos al trabajo, byteflair y acá tiene un ejemplo básico en la página de spring. En está actualización le agregué nuevos parámetros al archivo log4j.xml e ilustro la implementación de log4j con AOP.

Ahora si a codificar:

1.- Frameworks

  • Maven
  • Spring Framework – 4.3.3.RELEASE
  • Spring Security – 4.0.1.RELEASE
  • Hibernate – 5.1.2.Final
  • slf4j – 1.7.6
  • log4j – 1.2.17
  • Oracje JDBC – 11.2.0, como saben este driver es propietario por ende no se encuentra en los repos de maven, para su instalación pueden guiarse de acá (Install Oracle JDBC maven)
  • Spring Oauth2 – 2.0.11.RELEASE
  • Java JDK 1.7.0_72

2.- Servidor de Aplicaciones y base de datos

  • Apache Tomcat v8.0
  • Oracle XE 11g 64 Bits

3.- IDE

  • Eclipse Luna Service Release 2 (4.4.2)
  • Oracle Sql Developer 4.0.3.16

4.- Equipo de desarrollo

  • HP ProBook
  • Procesador: Intel Core i5 2.5 GHZ
  • Memoria Ram: 8 GB
  • SO: Windows 7 64 Bits

5.- Otros

  • Mozilla Firefox 43.0.4
  • FireFox Add On -> RestClient

6. Estructura del proyecto

El proyecto que contiene tanto el servidor de autenticación como el servidor de recursos tiene la siguiente estructura:

Estructura del Proyecto
Estructura del Proyecto

7.- Archivo POM(Project Object Model) – Maven

 <project xmlns="http://maven.apache.org/POM/4.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                              http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <groupId>MicroServices</groupId>
          <artifactId>MicroServices</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <packaging>war</packaging>

          <properties>
                   <springframework.version>4.3.3.RELEASE</springframework.version>
                   <springsecurity.version>4.0.1.RELEASE</springsecurity.version>
                   <spring.oauth2.version>2.0.11.RELEASE</spring.oauth2.version> 
                   <spring.hateaos.version>0.20.0.RELEASE</spring.hateaos.version>
                   <hibernate.version>5.1.2.Final</hibernate.version>
                   <slf4j.version>1.7.6</slf4j.version>
                   <log4j.version>1.2.17</log4j.version>
                   <oracle.version>11.2.0</oracle.version> 
                   <aspectj.version>1.7.4</aspectj.version>
                   <jackson.version>2.8.3</jackson.version>
          </properties>

          <dependencies>
                <!-- Spring -->
                <dependency>
                     <groupId>org.springframework</groupId>
                     <artifactId>spring-core</artifactId>
                     <version>${springframework.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-web</artifactId>
                    <version>${springframework.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-webmvc</artifactId>
                    <version>${springframework.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-tx</artifactId>
                    <version>${springframework.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-orm</artifactId>
                    <version>${springframework.version}</version>
               </dependency>

               <!-- Spring Security -->
               <dependency>
                    <groupId>org.springframework.security</groupId>
                    <artifactId>spring-security-web</artifactId>
                    <version>${springsecurity.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework.security</groupId>
                    <artifactId>spring-security-config</artifactId>
                    <version>${springsecurity.version}</version>
               </dependency>
               <dependency>
                    <groupId>org.springframework.security.oauth</groupId>
                    <artifactId>spring-security-oauth2</artifactId>
                    <version>${spring.oauth2.version}</version>
               </dependency>

               <!-- Spring AOP + AspectJ -->
               <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                    <version>${springframework.version}</version>
               </dependency>

               <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>${aspectj.version}</version>
                    <scope>runtime</scope>
               </dependency>

               <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
               </dependency>

               <!-- Spring Hateoas -->
               <dependency>
                    <groupId>org.springframework.hateoas</groupId>
                    <artifactId>spring-hateoas</artifactId>
                    <version>${spring.hateaos.version}</version>
               </dependency>

               <!-- Hibernate -->
               <dependency>
                    <groupId>org.hibernate</groupId>
                    <artifactId>hibernate-core</artifactId>
                    <version>${hibernate.version}</version>
               </dependency>
               <dependency>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                    <version>1.1.0.FINAL</version>
               </dependency>
               <dependency>
                    <groupId>org.hibernate</groupId>
                    <artifactId>hibernate-validator</artifactId>
                    <version>5.1.3.Final</version>
               </dependency>

               <!-- Oracle database driver -->
               <dependency>
                    <groupId>com.oracle</groupId>
                    <artifactId>ojdbc6</artifactId>
                    <version>${oracle.version}</version>
               </dependency>

               <!-- Servlet -->
               <dependency>
                    <groupId>javax.servlet</groupId>
                    <artifactId>javax.servlet-api</artifactId>
                    <version>3.1.0</version>
               </dependency>
               <dependency>
                    <groupId>javax.servlet.jsp</groupId>
                    <artifactId>javax.servlet.jsp-api</artifactId>
                    <version>2.3.1</version>
               </dependency>
               <dependency>
                    <groupId>javax.servlet</groupId>
                    <artifactId>jstl</artifactId>
                    <version>1.2</version>
               </dependency>

               <!-- Logger -->
               <dependency>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                    <version>${log4j.version}</version>
               </dependency>

               <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                    <version>${slf4j.version}</version>
               </dependency>

               <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                    <version>${slf4j.version}</version>
               </dependency>

               <!-- Jackson -->
               <dependency>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-databind</artifactId>
                    <version>${jackson.version}</version>
              </dependency>
          </dependencies>

          <build>
              <sourceDirectory>src</sourceDirectory>
              <plugins>
                  <plugin>
                       <artifactId>maven-war-plugin</artifactId>
                       <version>2.4</version>
                       <configuration>
                              <warSourceDirectory>WebContent</warSourceDirectory>
                              <failOnMissingWebXml>false</failOnMissingWebXml>
                       </configuration>
                  </plugin>
                  <plugin>
                       <artifactId>maven-compiler-plugin</artifactId>
                       <version>3.3</version>
                       <configuration>
                               <source>1.7</source>
                               <target>1.7</target>
                        </configuration>
                  </plugin>
              </plugins>
          </build>
</project>

8.- Schema de base de datos


-- SCHEMA FACTURACION
-- AUTOR: WILMAN ORTIZ
-- FECHA: 24 AGO 2015

-- ELIMINAR TABLAS
DROP TABLE USERS CASCADE CONSTRAINT;
DROP TABLE ROLES CASCADE CONSTRAINT;
DROP TABLE USERS_ROLES CASCADE CONSTRAINT;
DROP TABLE CLIENTES CASCADE CONSTRAINT;
DROP TABLE PRODUCTO CASCADE CONSTRAINT;
DROP TABLE FACTURA CASCADE CONSTRAINT;
DROP TABLE DETALLE_FACTURA CASCADE CONSTRAINT;
DROP TABLE IMAGEN CASCADE CONSTRAINT;
DROP TABLE OAUTH_CLIENT_DETAILS CASCADE CONSTRAINT;
DROP TABLE OAUTH_CLIENT_TOKEN CASCADE CONSTRAINT;
DROP TABLE OAUTH_ACCESS_TOKEN CASCADE CONSTRAINT;
DROP TABLE OAUTH_REFRESH_TOKEN CASCADE CONSTRAINT;
DROP TABLE OAUTH_CODE CASCADE CONSTRAINT;
DROP TABLE OAUTH_APPROVALS CASCADE CONSTRAINT;
DROP TABLE CLIENT_DETAILS CASCADE CONSTRAINT;
DROP SEQUENCE SQ_USERS;
DROP SEQUENCE SQ_ROLES;
DROP SEQUENCE SQ_PRODUCTO;
DROP SEQUENCE SQ_FACTURA;
DROP SEQUENCE SQ_CLIENTES;
DROP SEQUENCE SQ_IMAGEN;

-- DROP TRIGGER TRG_USERS;
-- DROP TRIGGER TRG_ROLES;
-- DROP TRIGGER TRG_PRODUCTO;
-- DROP TRIGGER TRG_FACTURA;

-- CREANDO TABLAS
CREATE TABLE USERS(
    ID_USER NUMBER NOT NULL,
    CEDULA NUMBER NOT NULL,
    NOMBRE VARCHAR2(50) NOT NULL,
    APELLIDO VARCHAR2(50) NOT NULL,
    DIRECCION VARCHAR2(100),
    FECHA_NACIMIENTO DATE,
    FECHA_INGRESO DATE,
    FECHA_RETIRO DATE,
    TELEFONO VARCHAR2(30),
    EMAIL VARCHAR2(100) NOT NULL,
    LOGIN VARCHAR2(30) NOT NULL,
    PASSWORD VARCHAR2(100) NOT NULL,
    ENABLE VARCHAR2(10),
    ACCOUNT_NON_EXPIRED VARCHAR2(10),
    CREDENTIAL_NON_EXPIRED VARCHAR2(10),
    ACCOUNT_NON_LOCKED VARCHAR2(10),
    ID_IMG NUMBER
);

CREATE TABLE ROLES(
    ID_ROLE NUMBER NOT NULL,
    ROLE VARCHAR2(50) NOT NULL
);

CREATE TABLE USERS_ROLES(
    USER_ID NUMBER NOT NULL,
    ROLE_ID NUMBER NOT NULL
);

CREATE TABLE OAUTH_CLIENT_DETAILS (
       CLIENT_ID VARCHAR2(255) PRIMARY KEY,
       RESOURCE_IDS VARCHAR2(255),
       CLIENT_SECRET VARCHAR2(255),
       SCOPE VARCHAR2(255),
       AUTHORIZED_GRANT_TYPES VARCHAR2(255),
       WEB_SERVER_REDIRECT_URI VARCHAR2(255),
       AUTHORITIES VARCHAR2(255),
       ACCESS_TOKEN_VALIDITY NUMBER(10,0),
       REFRESH_TOKEN_VALIDITY NUMBER(10,0),
       ADDITIONAL_INFORMATION VARCHAR2(2000),
       AUTOAPPROVE VARCHAR2(255)
);

CREATE TABLE OAUTH_CLIENT_TOKEN (
       AUTHENTICATION_ID VARCHAR2(255) PRIMARY KEY,
       TOKEN_ID VARCHAR2(255),
       TOKEN BLOB, 
       USER_NAME VARCHAR2(255),
       CLIENT_ID VARCHAR2(255)
);

CREATE TABLE OAUTH_ACCESS_TOKEN (
       AUTHENTICATION_ID VARCHAR2(255) PRIMARY KEY,
       TOKEN_ID VARCHAR2(255),
       TOKEN BLOB, 
       USER_NAME VARCHAR2(255),
       CLIENT_ID VARCHAR2(255),
       AUTHENTICATION BLOB,
       REFRESH_TOKEN VARCHAR2(255)
);

CREATE TABLE OAUTH_REFRESH_TOKEN (
       TOKEN_ID VARCHAR2(255),
       TOKEN BLOB,
       AUTHENTICATION BLOB
);

CREATE TABLE OAUTH_CODE (
       CODE VARCHAR2(255), 
       AUTHENTICATION BLOB
);

CREATE TABLE OAUTH_APPROVALS (
       USERID VARCHAR2(255),
       CLIENTID VARCHAR2(255),
       SCOPE VARCHAR2(255),
       STATUS VARCHAR2(10),
       EXPIRESAT DATE,
       LASTMODIFIEDAT DATE
);

CREATE TABLE CLIENT_DETAILS (
       APPID VARCHAR2(255) PRIMARY KEY,
       RESOURCEIDS VARCHAR2(255),
       APPSECRET VARCHAR2(255),
       SCOPE VARCHAR2(255),
       GRANTTYPES VARCHAR2(255),
       REDIRECTURL VARCHAR2(255),
       AUTHORITIES VARCHAR2(255),
       ACCESS_TOKEN_VALIDITY NUMBER(10,0),
       REFRESH_TOKEN_VALIDITY NUMBER(10,0),
       ADDITIONALINFORMATION VARCHAR2(2000),
       AUTOAPPROVESCOPES VARCHAR2(255)
);
CREATE TABLE CLIENTES(
    ID_CLIENTE NUMBER NOT NULL,
    CEDULA NUMBER NOT NULL,
    NOMBRE VARCHAR2(50) NOT NULL,
    APELLIDO VARCHAR2(50) NOT NULL,
    DIRECCION VARCHAR2(100),
    FECHA_NACIMIENTO DATE,
    TELEFONO VARCHAR2(30),
    EMAIL VARCHAR2(100) NOT NULL,
    ID_IMG NUMBER
);

CREATE TABLE PRODUCTO(
    ID_PRODUCTO NUMBER NOT NULL,
    CODIGO VARCHAR2(100) NOT NULL,
    NOMBRE VARCHAR2(100) NOT NULL,
    PRECIO NUMBER NOT NULL,
    STOCK NUMBER NOT NULL
);

CREATE TABLE FACTURA(
    NUMERO_FACTURA NUMBER NOT NULL,
    FECHA DATE NOT NULL,
    ID_CLIENTE NUMBER NOT NULL
);

CREATE TABLE DETALLE_FACTURA(
    NUM_DETALLE NUMBER NOT NULL,
    NUMERO_FACTURA NUMBER NOT NULL,
    ID_PRODUCTO NUMBER NOT NULL,
    CANTIDAD NUMBER NOT NULL,
    PRECIO NUMBER NOT NULL
);

CREATE TABLE IMAGEN(
    ID_IMAGEN NUMBER NOT NULL,
    IMG_PATH VARCHAR2(512) NOT NULL,
    IMG_NAME VARCHAR2(256) NOT NULL,
    IMG_SIZE VARCHAR2(100) NOT NULL,
    IMG_TYPE VARCHAR2(100) NOT NULL
);

-- EJECUTANDO CONSTRAINT
ALTER TABLE USERS ADD CONSTRAINT USER_PK PRIMARY KEY (ID_USER);

ALTER TABLE USERS ADD CONSTRAINT USER_LG_UK UNIQUE (LOGIN);
ALTER TABLE USERS ADD CONSTRAINT USER_CC_UK UNIQUE (CEDULA);

ALTER TABLE ROLES ADD CONSTRAINT ROLE_PK PRIMARY KEY(ID_ROLE);

ALTER TABLE USERS_ROLES ADD CONSTRAINT USER_ROLE_PK PRIMARY KEY (USER_ID, ROLE_ID);
ALTER TABLE USERS_ROLES ADD CONSTRAINT USER_FK FOREIGN KEY (USER_ID) REFERENCES USERS(ID_USER);
ALTER TABLE USERS_ROLES ADD CONSTRAINT ROLE_FK FOREIGN KEY (ROLE_ID) REFERENCES ROLES(ID_ROLE);

ALTER TABLE IMAGEN ADD CONSTRAINT IMAGEN_PK PRIMARY KEY (ID_IMAGEN);

ALTER TABLE USERS ADD CONSTRAINT USER_IMAGEN_FK FOREIGN KEY (ID_IMG) REFERENCES IMAGEN(ID_IMAGEN);

ALTER TABLE CLIENTES ADD CONSTRAINT CLIENTE_PK PRIMARY KEY (ID_CLIENTE);
ALTER TABLE CLIENTES ADD CONSTRAINT CLIENTE_CC_UK UNIQUE (CEDULA);
ALTER TABLE CLIENTES ADD CONSTRAINT IMAGEN_FK FOREIGN KEY (ID_IMG) REFERENCES IMAGEN (ID_IMAGEN);

ALTER TABLE PRODUCTO ADD CONSTRAINT PRODUCTO_PK PRIMARY KEY (ID_PRODUCTO);
ALTER TABLE PRODUCTO ADD CONSTRAINT PRODUCTO_COD_UK UNIQUE (CODIGO);

ALTER TABLE FACTURA ADD CONSTRAINT FACTURA_PK PRIMARY KEY (NUMERO_FACTURA);
ALTER TABLE FACTURA ADD CONSTRAINT CLIENTE_FK FOREIGN KEY (ID_CLIENTE) REFERENCES CLIENTES(ID_CLIENTE);

ALTER TABLE DETALLE_FACTURA ADD CONSTRAINT DETALLE_FACTURA_PK PRIMARY KEY (NUM_DETALLE, NUMERO_FACTURA);
ALTER TABLE DETALLE_FACTURA ADD CONSTRAINT FACTURA_DETALLE_FK FOREIGN KEY (NUMERO_FACTURA) REFERENCES FACTURA(NUMERO_FACTURA);
ALTER TABLE DETALLE_FACTURA ADD CONSTRAINT PRODUCTO_DETALLE_FK FOREIGN KEY (ID_PRODUCTO) REFERENCES PRODUCTO(ID_PRODUCTO);

-- CREANDO SECUENCIAS
CREATE SEQUENCE SQ_USERS INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_ROLES INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_PRODUCTO INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_FACTURA INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_CLIENTES INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_IMAGEN INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
CREATE SEQUENCE SQ_DETALLE_FACTURA INCREMENT BY 1 START WITH 1 MAXVALUE 999999999999999999999 MINVALUE 1;
-- CREANDO TRIGGERS
CREATE OR REPLACE TRIGGER TRG_USERS BEFORE INSERT ON USERS
 FOR EACH ROW
 BEGIN
    IF (:NEW.ID_USER IS NULL) THEN
        SELECT SQ_USERS.NEXTVAL INTO :NEW.ID_USER
        FROM DUAL;
    END IF;
 END;
/ 

CREATE OR REPLACE TRIGGER TRG_ROLES BEFORE INSERT ON ROLES
 FOR EACH ROW
 BEGIN
    IF (:NEW.ID_ROLE IS NULL) THEN
        SELECT SQ_ROLES.NEXTVAL INTO :NEW.ID_ROLE
        FROM DUAL;
    END IF;
 END;
/

CREATE OR REPLACE TRIGGER TRG_IMAGEN BEFORE INSERT ON IMAGEN
 FOR EACH ROW
 BEGIN
    IF (:NEW.ID_IMAGEN IS NULL) THEN
        SELECT SQ_IMAGEN.NEXTVAL INTO :NEW.ID_IMAGEN
        FROM DUAL;
    END IF;
 END;
/

CREATE OR REPLACE TRIGGER TRG_CLIENTES BEFORE INSERT ON CLIENTES
 FOR EACH ROW
 BEGIN
    IF (:NEW.ID_CLIENTE IS NULL) THEN
        SELECT SQ_CLIENTES.NEXTVAL INTO :NEW.ID_CLIENTE
        FROM DUAL;
    END IF;
 END;
/ 

CREATE OR REPLACE TRIGGER TRG_PRODUCTO BEFORE INSERT ON PRODUCTO
 FOR EACH ROW
 BEGIN
    IF (:NEW.ID_PRODUCTO IS NULL) THEN
        SELECT SQ_PRODUCTO.NEXTVAL INTO :NEW.ID_PRODUCTO
        FROM DUAL;
    END IF;
 END;
/

CREATE OR REPLACE TRIGGER TRG_FACTURA BEFORE INSERT ON FACTURA
 FOR EACH ROW
 BEGIN
    IF (:NEW.NUMERO_FACTURA IS NULL) THEN
        SELECT SQ_FACTURA.NEXTVAL INTO :NEW.NUMERO_FACTURA
        FROM DUAL;
    END IF;
 END;
/

ALTER TRIGGER TRG_USERS ENABLE;
ALTER TRIGGER TRG_ROLES ENABLE;
ALTER TRIGGER TRG_PRODUCTO ENABLE;
ALTER TRIGGER TRG_FACTURA ENABLE;

-- INSERTANDO VALORES POR DEFECTO
INSERT INTO OAUTH_CLIENT_DETAILS (CLIENT_ID,RESOURCE_IDS,CLIENT_SECRET,SCOPE,AUTHORIZED_GRANT_TYPES,WEB_SERVER_REDIRECT_URI,AUTHORITIES,ACCESS_TOKEN_VALIDITY,REFRESH_TOKEN_VALIDITY,ADDITIONAL_INFORMATION,AUTOAPPROVE) VALUES ('clientapp','microservice','987654321','read,write','password,refresh_token,client_credentials','http://localhost:8086/MicroServices/','USER','300','600',null,null);

INSERT INTO USERS(CEDULA, NOMBRE, APELLIDO, DIRECCION, FECHA_NACIMIENTO, FECHA_INGRESO, FECHA_RETIRO, TELEFONO, EMAIL, LOGIN, PASSWORD, ENABLE, ACCOUNT_NON_EXPIRED, CREDENTIAL_NON_EXPIRED, ACCOUNT_NON_LOCKED, ID_IMG) VALUES (8649168, 'root', 'admin', 'Carrera 123', TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),3166178398,'root@gmail.com','root','$2a$10$tartLAsO2Bm8iMtrqdyQXua/CMZ3/8260G/c4OKza6DslRKexKSuW','true','true','true','true',null);
INSERT INTO USERS(CEDULA, NOMBRE, APELLIDO, DIRECCION, FECHA_NACIMIENTO, FECHA_INGRESO, FECHA_RETIRO, TELEFONO, EMAIL, LOGIN, PASSWORD, ENABLE, ACCOUNT_NON_EXPIRED, CREDENTIAL_NON_EXPIRED, ACCOUNT_NON_LOCKED, ID_IMG) VALUES (1043000687, 'Juan', 'Ortiz', 'Av. Siempre viva 742', TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),3016394477,'jortiz@hotmail.com','jortiz','$2a$10$tartLAsO2Bm8iMtrqdyQXua/CMZ3/8260G/c4OKza6DslRKexKSuW','true','true','true','true',null);
INSERT INTO USERS(CEDULA, NOMBRE, APELLIDO, DIRECCION, FECHA_NACIMIENTO, FECHA_INGRESO, FECHA_RETIRO, TELEFONO, EMAIL, LOGIN, PASSWORD, ENABLE, ACCOUNT_NON_EXPIRED, CREDENTIAL_NON_EXPIRED, ACCOUNT_NON_LOCKED, ID_IMG) VALUES (123456789, 'Guest' , 'User', 'NOT_FOUND', TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),TO_DATE(SYSDATE,'dd/MM/yyyy'),3016390440,'guest@example.com','guest','$2a$10$tartLAsO2Bm8iMtrqdyQXua/CMZ3/8260G/c4OKza6DslRKexKSuW','true','true','true','true',null);

INSERT INTO ROLES(ROLE) VALUES ('ROLE_ADMIN');
INSERT INTO ROLES(ROLE) VALUES ('ROLE_USER');
INSERT INTO ROLES(ROLE) VALUES ('ROLE_GUEST');

INSERT INTO USERS_ROLES(USER_ID, ROLE_ID) VALUES (1, 1);
INSERT INTO USERS_ROLES(USER_ID, ROLE_ID) VALUES (1, 2);
INSERT INTO USERS_ROLES(USER_ID, ROLE_ID) VALUES (2, 2);
INSERT INTO USERS_ROLES(USER_ID, ROLE_ID) VALUES (3, 3);

INSERT INTO APPLICATION.CLIENTES (ID_CLIENTE,CEDULA,NOMBRE,APELLIDO,DIRECCION,FECHA_NACIMIENTO,TELEFONO,EMAIL,ID_IMG) VALUES ('1','123456','Juan','Perez','Calle 123',to_date('05/07/93','DD/MM/RR'),'03166178398','karlos@hotmail.com',null);
INSERT INTO APPLICATION.CLIENTES (ID_CLIENTE,CEDULA,NOMBRE,APELLIDO,DIRECCION,FECHA_NACIMIENTO,TELEFONO,EMAIL,ID_IMG) VALUES ('2','789987','Alvaro','Otalora','Calle 456',to_date('05/07/10','DD/MM/RR'),'987654','gene@gmail.com',null);
INSERT INTO APPLICATION.CLIENTES (ID_CLIENTE,CEDULA,NOMBRE,APELLIDO,DIRECCION,FECHA_NACIMIENTO,TELEFONO,EMAIL,ID_IMG) VALUES ('3','4567654999','Pibe','Valderrama','Calle XYZ',to_date('04/07/12','DD/MM/RR'),'87654377','chary123@hotmail.com',null);
INSERT INTO APPLICATION.CLIENTES (ID_CLIENTE,CEDULA,NOMBRE,APELLIDO,DIRECCION,FECHA_NACIMIENTO,TELEFONO,EMAIL,ID_IMG) VALUES ('4','9191919','Tino','Asprilla','Calle 321',to_date('03/03/40','DD/MM/RR'),'765432','ram@yahoo.es',null);
INSERT INTO APPLICATION.CLIENTES (ID_CLIENTE,CEDULA,NOMBRE,APELLIDO,DIRECCION,FECHA_NACIMIENTO,TELEFONO,EMAIL,ID_IMG) VALUES ('6','9292923','Andres','Iniesta','Cra 123',to_date('19/09/84','DD/MM/RR'),'876543','maria@hotmail.com',null);

INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('001','BOARD INTEL MODELO 2015',300000,35);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('002','BOARD ASUS MODELO 2015',500000,20);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('003','BOARD MSI MODELO 2015',400000,15);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('004','TECLADO ASUS ROG MODELO 2015',30000,50);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('005','TECLADO MSI GAMER MODELO 2015',45000,10);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('006','TECLADO BASICO MODELO 2015',20000,20);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('007','MOUSE GENIUS MODELO 2015',13000,50);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('008','MOUSE GENIUS GAMER MODELO 2015',80000,5);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('009','MEMORIA USB 8 GB',15000,12);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0010','MEMORIA USB 16 GB',30000,35);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0011','MEMORIA USB 32 GB',45000,25);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0012','MEMORIA RAM 4 GB',70000,9);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0013','MEMORIA RAM 8 GB',110000,7);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0014','MEMORIA RAM 16 GB',200000,4);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0015','MONITOR SANSUMG 15 PULGADAS',300000,18);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0016','MONITOR LG 15 PULGADAS',700000,10);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0017','PORTATIL HP 1870',1200000,3);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0018','PORTATIL ASUS GX500',5560000,2);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0019','PORTATIL LENOVO THINKPAD',1800000,4);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0020','DISCO DURO EXTERNO HITACHI 2 TB',200000,20);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0021','DISCO DURO EXTERNO SANSUMG 1 TB',180000,15);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0022','DISCO DURO EXTERNO INTEL 512 GB',120000,35);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0023','CELULAR SANSUMG S6',2600000,11);
INSERT INTO PRODUCTO (NOMBRE, CODIGO, PRECIO, STOCK) VALUES ('0024','CELULAR XIAOMI MI NOTE PRO',1860000,4);

INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('1',TO_DATE('29/09/16','DD/MM/RR'),'1');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('2',TO_DATE('07/09/16','DD/MM/RR'),'1');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('3',TO_DATE('10/09/16','DD/MM/RR'),'1');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('4',TO_DATE('05/09/16','DD/MM/RR'),'1');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('5',TO_DATE('01/09/16','DD/MM/RR'),'2');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('6',TO_DATE('04/09/16','DD/MM/RR'),'2');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('7',TO_DATE('14/09/16','DD/MM/RR'),'2');
INSERT INTO FACTURA (NUMERO_FACTURA,FECHA,ID_CLIENTE) VALUES ('8',TO_DATE('23/09/16','DD/MM/RR'),'2');

INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('1','1','2','3','10000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('2','1','4','2','50000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('3','1','6','4','6600');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('4','2','1','1','56200');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('5','2','7','5','23000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('6','3','41','1','45000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('7','3','11','4','60000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('8','3','8','7','15000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('9','4','20','1','100000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('10','5','4','2','90000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('11','5','6','2','7000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('12','6','18','11','30000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('13','6','19','2','64000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('14','7','9','3','85000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('15','8','14','6','90000');
INSERT INTO DETALLE_FACTURA (NUM_DETALLE,NUMERO_FACTURA,ID_PRODUCTO,CANTIDAD,PRECIO) VALUES ('16','8','13','7','75300');

CREATE OR REPLACE PACKAGE PKG_VENTAS IS
 PROCEDURE SP_OBTENER_CEDULA(ipNOMBRE IN VARCHAR2, opCEDULA OUT NUMBER);
 PROCEDURE SP_CONSULTA_USUARIOS(opLISTADO OUT SYS_REFCURSOR, ipTIPO IN VARCHAR2);
END;
/

CREATE OR REPLACE PACKAGE BODY PKG_VENTAS IS

 PROCEDURE SP_OBTENER_CEDULA(ipNOMBRE IN VARCHAR2, opCEDULA OUT NUMBER) AS
 BEGIN
    SELECT C.CEDULA INTO opCEDULA
    FROM CLIENTES C
    WHERE C.NOMBRE LIKE '%' || ipNOMBRE || '%';
 EXCEPTION
    WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('ERROR SP_OBTENER_CEDULA');
 END;

 PROCEDURE SP_CONSULTA_USUARIOS(opLISTADO OUT SYS_REFCURSOR, ipTIPO IN VARCHAR2) AS
 BEGIN
    OPEN opLISTADO FOR
     SELECT c.cedula,
            c.nombre,
            c.apellido,
            im.img_path,
            im.img_name
     FROM Clientes c INNER JOIN (SELECT i.id_imagen codigo,
                                        i.img_path,
                                        i.img_name
                                 FROM Imagen i
                                 WHERE i.img_type = ipTIPO) im ON (c.id_img = im.codigo);
 EXCEPTION
      WHEN OTHERS THEN
           DBMS_OUTPUT.PUT_LINE('ERROR EJECUTANDO EL PROCEDIMIENTO');
 END;
END;
/

9.- Encriptación de password

Antes de comenzar crearemos una pequeña clase cuyo objetivo es crear los password para nuestros usuarios con los que inicia nuestro sistema, podríamos crear una web de registro de usuarios pero por practicidad crearemos un main, spring security viene de caja con implementación de varios algoritmos de encriptación como MD5, SHA1, para nuestro ejercicio usaremos BCrypt en las últimas versiones de spring security el constructor de la clase BCryptPasswordEncoder ya viene con la opción de configurar el SALT que viene siendo una especie de header que se encripta junto con el valor de nuestro password.

Creación de Password
Creación de Password

10.- Creación del Modelo

Del esquema de base de datos cargado anteriormente solo usaremos unas cuantas tablas (Cliente, Producto, User, Rol), crearemos una entidad base:

10.1.- BaseEntity.java


package co.com.microservice.application.model;

import java.io.Serializable;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public class BaseEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;

    public Long getId() {
       return id;
    }

    public void setId(Long id) {
       this.id = id;
    }

}

10.2.- User.java

package co.com.microservice.application.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "USERS", catalog = "", schema = "APPLICATION")
@NamedQueries({
               @NamedQuery(name = "User.findAll" , query = "SELECT u FROM User u"),
               @NamedQuery(name = "User.findByIdUser" , query = "SELECT u FROM User u WHERE u.idUser = :idUser"),
               @NamedQuery(name = "User.findByCedula" , query = "SELECT u FROM User u WHERE u.cedula = :cedula"),
               @NamedQuery(name = "User.findByNombre" , query = "SELECT u FROM User u WHERE u.nombre = :nombre"),
               @NamedQuery(name = "User.findByApellido" , query = "SELECT u FROM User u WHERE u.apellido = :apellido"),
               @NamedQuery(name = "User.findByDireccion" , query = "SELECT u FROM User u WHERE u.direccion = :direccion"),
               @NamedQuery(name = "User.findByFechaNacimiento" , query = "SELECT u FROM User u WHERE u.fechaNacimiento = :fechaNacimiento"),
               @NamedQuery(name = "User.findByFechaIngreso" , query = "SELECT u FROM User u WHERE u.fechaIngreso = :fechaIngreso"),
               @NamedQuery(name = "User.findByFechaRetiro" , query = "SELECT u FROM User u WHERE u.fechaRetiro = :fechaRetiro"),
               @NamedQuery(name = "User.findByTelefono" , query = "SELECT u FROM User u WHERE u.telefono = :telefono"),
               @NamedQuery(name = "User.findByEmail" , query = "SELECT u FROM User u WHERE u.email = :email"),
               @NamedQuery(name = "User.findByLogin" , query = "SELECT u FROM User u WHERE u.login = :login"),
               @NamedQuery(name = "User.findByPassword" , query = "SELECT u FROM User u WHERE u.password = :password"),
               @NamedQuery(name = "User.findByEnable" , query = "SELECT u FROM User u WHERE u.enable = :enable"),
               @NamedQuery(name = "User.findByAccountNonExpired" , query = "SELECT u FROM User u WHERE u.accountNonExpired = :accountNonExpired"),
               @NamedQuery(name = "User.findByCredentialNonExpired", query = "SELECT u FROM User u WHERE u.credentialNonExpired = :credentialNonExpired"),
               @NamedQuery(name = "User.findByAccountNonLocked" , query = "SELECT u FROM User u WHERE u.accountNonLocked = :accountNonLocked")})
public class User implements Serializable {

        private static final long serialVersionUID = 1L;

        // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
        @Id
        @Basic(optional = false)
        @NotNull
        @Column(name = "ID_USER")
        @SequenceGenerator(name = "sec_user", sequenceName = "SQ_USERS", allocationSize = 1)
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="sec_user")
        private BigDecimal idUser;

        @Basic(optional = false)
        @NotNull
        @Column(name = "CEDULA")
        private BigInteger cedula;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 50)
        @Column(name = "NOMBRE")
        private String nombre;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 50)
        @Column(name = "APELLIDO")
        private String apellido;

        @Size(max = 100)
        @Column(name = "DIRECCION")
        private String direccion;

        @Column(name = "FECHA_NACIMIENTO")
        @Temporal(TemporalType.TIMESTAMP)
        private Date fechaNacimiento;

        @Column(name = "FECHA_INGRESO")
        @Temporal(TemporalType.TIMESTAMP)
        private Date fechaIngreso;

        @Column(name = "FECHA_RETIRO")
        @Temporal(TemporalType.TIMESTAMP)
        private Date fechaRetiro;

        @Size(max = 30)
        @Column(name = "TELEFONO")
        private String telefono;

        // @Pattern(regexp="[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 100)
        @Column(name = "EMAIL")
        private String email;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 30)
        @Column(name = "LOGIN")
        private String login;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 100)
        @Column(name = "PASSWORD")
        private String password;

        @Size(max = 10)
        @Column(name = "ENABLE")
        private String enable;

        @Size(max = 10)
        @Column(name = "ACCOUNT_NON_EXPIRED")
        private String accountNonExpired;

        @Size(max = 10)
        @Column(name = "CREDENTIAL_NON_EXPIRED")
        private String credentialNonExpired;

        @Size(max = 10)
        @Column(name = "ACCOUNT_NON_LOCKED")
        private String accountNonLocked;

        @ManyToMany(mappedBy = "userList", fetch = FetchType.LAZY)
        private List<Role> roleList;

        public User() {
        } 

    public User(BigDecimal idUser) {
        this.idUser = idUser;
    }

     public User(BigDecimal idUser, BigInteger cedula, String nombre, String apellido, String email, String login, String password) {
        this.idUser = idUser;
        this.cedula = cedula;
        this.nombre = nombre;
        this.apellido = apellido;
        this.email = email;
        this.login = login;
        this.password = password;
     }

     public BigDecimal getIdUser() {
        return idUser;
     }

     public void setIdUser(BigDecimal idUser) {
        this.idUser = idUser;
     }

     public BigInteger getCedula() {
        return cedula;
     }

     public void setCedula(BigInteger cedula) {
        this.cedula = cedula;
     }

     public String getNombre() {
         return nombre;
     }

     public void setNombre(String nombre) {
         this.nombre = nombre;
     }

     public String getApellido() {
         return apellido;
     }

     public void setApellido(String apellido) {
         this.apellido = apellido;
     }

     public String getDireccion() {
         return direccion;
     }

     public void setDireccion(String direccion) {
         this.direccion = direccion;
     }

     public Date getFechaNacimiento() {
         return fechaNacimiento;
     }

     public void setFechaNacimiento(Date fechaNacimiento) {
         this.fechaNacimiento = fechaNacimiento;
     }

     public Date getFechaIngreso() {
         return fechaIngreso;
     }

     public void setFechaIngreso(Date fechaIngreso) {
          this.fechaIngreso = fechaIngreso;
     }

     public Date getFechaRetiro() {
          return fechaRetiro;
     }

     public void setFechaRetiro(Date fechaRetiro) {
         this.fechaRetiro = fechaRetiro;
     }

     public String getTelefono() {
         return telefono;
     }

     public void setTelefono(String telefono) {
         this.telefono = telefono;
     }

     public String getEmail() {
         return email;
     }

     public void setEmail(String email) {
         this.email = email;
     }

     public String getLogin() {
        return login;
     }

     public void setLogin(String login) {
         this.login = login;
     }

     public String getPassword() {
         return password;
     }

     public void setPassword(String password) {
         this.password = password;
     }

     public String getEnable() {
         return enable;
     }

     public void setEnable(String enable) {
         this.enable = enable;
     }

     public String getAccountNonExpired() {
         return accountNonExpired;
     }

     public void setAccountNonExpired(String accountNonExpired) {
         this.accountNonExpired = accountNonExpired;
     }

     public String getCredentialNonExpired() {
         return credentialNonExpired;
     }

     public void setCredentialNonExpired(String credentialNonExpired) {
          this.credentialNonExpired = credentialNonExpired;
     }

     public String getAccountNonLocked() {
          return accountNonLocked;
     }

     public void setAccountNonLocked(String accountNonLocked) {
          this.accountNonLocked = accountNonLocked;
     }

     public List<Role> getRoleList() {
            return roleList;
     }

     public void setRoleList(List<Role> roleList) {
            this.roleList = roleList;
     }

     @Override
     public int hashCode() {
       int hash = 0;
       hash += (idUser != null ? idUser.hashCode() : 0);
       return hash;
     }

     @Override
     public boolean equals(Object object) {
            if (!(object instanceof User)) {
                return false;
            }
            User other = (User) object;
            if ((this.idUser == null && other.idUser != null) || (this.idUser != null && !this.idUser.equals(other.idUser))) {
                return false;
            }
            return true;
     }

     @Override
     public String toString() {
          return "modelo.User[ idUser=" + idUser + " ]";
     }

}

10.2.- Role.java

package co.com.microservice.application.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "ROLES", catalog = "", schema = "APPLICATION")
@NamedQueries({
               @NamedQuery(name = "Role.findAll" , query = "SELECT r FROM Role r"),
               @NamedQuery(name = "Role.findByIdRole", query = "SELECT r FROM Role r WHERE r.idRole = :idRole"),
               @NamedQuery(name = "Role.findByRole" , query = "SELECT r FROM Role r WHERE r.role = :role")})
public class Role implements Serializable {

         private static final long serialVersionUID = 1L;

         // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
         @Id
         @Basic(optional = false)
         @NotNull
         @Column(name = "ID_ROLE")
         @SequenceGenerator(name = "sec_roles", sequenceName = "SQ_ROLES", allocationSize = 1)
         @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="sec_roles")
         private BigDecimal idRole;

         @Basic(optional = false)
         @NotNull
         @Size(min = 1, max = 50)
         @Column(name = "ROLE")
         private String role;

         @JoinTable(name = "USERS_ROLES", joinColumns = {
                                                         @JoinColumn(name = "ROLE_ID", referencedColumnName = "ID_ROLE")}, inverseJoinColumns = {
                                                         @JoinColumn(name = "USER_ID", referencedColumnName = "ID_USER")})
         @ManyToMany(fetch = FetchType.LAZY)
         private List<User> userList;

          public Role() {
          }

          public Role(BigDecimal idRole) {
             this.idRole = idRole;
          }

           public Role(BigDecimal idRole, String role) {
                 this.idRole = idRole;
                 this.role = role;
           }
 
           public BigDecimal getIdRole() {
                 return idRole;
           }

           public void setIdRole(BigDecimal idRole) {
                 this.idRole = idRole;
           }

           public String getRole() {
                 return role;
           }

           public void setRole(String role) {
                 this.role = role;
           }

           public List<User> getUserList() {
                 return userList;
           }

           public void setUserList(List<User>; userList) {
                 this.userList = userList;
           }

           @Override
           public int hashCode() {
                 int hash = 0;
                 hash += (idRole != null ? idRole.hashCode() : 0);
                 return hash;
           }

           @Override
           public boolean equals(Object object) {
                  if (!(object instanceof Role)) {
                      return false;
                  }
                  Role other = (Role) object;
                  if ((this.idRole == null && other.idRole != null) || (this.idRole != null && !this.idRole.equals(other.idRole))) {
                       return false;
                  }
                  return true;
           }

           @Override
           public String toString() {
                  return "modelo.Role[ idRole=" + idRole + " ]";
           }

}

10.3.- Cliente.java

package co.com.microservice.application.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.codehaus.jackson.map.annotate.JsonSerialize;

import co.com.microservice.application.utils.JsonDateSerializer;

@Entity
@Table(name = "CLIENTES", catalog = "", schema = "APPLICATION")
@NamedQueries({
              @NamedQuery(name = "Cliente.findAll" , query = "SELECT c FROM Cliente c"),
              @NamedQuery(name = "Cliente.findByIdCliente" , query = "SELECT c FROM Cliente c WHERE c.idCliente = :idCliente"),
              @NamedQuery(name = "Cliente.findByCedula" , query = "SELECT c FROM Cliente c WHERE c.cedula = :cedula"),
              @NamedQuery(name = "Cliente.findByNombre" , query = "SELECT c FROM Cliente c WHERE c.nombre = :nombre"),
              @NamedQuery(name = "Cliente.findByApellido" , query = "SELECT c FROM Cliente c WHERE c.apellido = :apellido"),
              @NamedQuery(name = "Cliente.findByDireccion" , query = "SELECT c FROM Cliente c WHERE c.direccion = :direccion"),
              @NamedQuery(name = "Cliente.findByFechaNacimiento", query = "SELECT c FROM Cliente c WHERE c.fechaNacimiento = :fechaNacimiento"),
              @NamedQuery(name = "Cliente.findByTelefono" , query = "SELECT c FROM Cliente c WHERE c.telefono = :telefono"),
              @NamedQuery(name = "Cliente.findByEmail" , query = "SELECT c FROM Cliente c WHERE c.email = :email")})
public class Cliente implements Serializable {

        private static final long serialVersionUID = 1L;

        // @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
        @Id
        @Basic(optional = false)
        @NotNull
        @Column(name = "ID_CLIENTE")
        @SequenceGenerator(name = "sec_cliente", sequenceName = "SQ_CLIENTES", allocationSize = 1)
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="sec_cliente")
        private BigDecimal idCliente;

        @Basic(optional = false)
        @NotNull
        @Column(name = "CEDULA")
        private BigInteger cedula;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 50)
        @Column(name = "NOMBRE")
        private String nombre;

        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 50)
        @Column(name = "APELLIDO")
        private String apellido;

        @Size(max = 100)
        @Column(name = "DIRECCION")
        private String direccion;

        @Column(name = "FECHA_NACIMIENTO")
        @Temporal(TemporalType.DATE)
        @JsonSerialize(using=JsonDateSerializer.class)
        private Date fechaNacimiento;

        @Size(max = 30)
        @Column(name = "TELEFONO")
        private String telefono;

        // @Pattern(regexp="[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 100)
        @Column(name = "EMAIL")
        private String email;

        public Cliente() {
        }

        public Cliente(BigDecimal idCliente) {
             this.idCliente = idCliente;
        }

        public Cliente(BigDecimal idCliente, BigInteger cedula, String nombre, String apellido, String email) {
                this.idCliente = idCliente;
                this.cedula = cedula;
                this.nombre = nombre;
                this.apellido = apellido;
                this.email = email;
        }

        public BigDecimal getIdCliente() {
               return idCliente;
        }

        public void setIdCliente(BigDecimal idCliente) {
               this.idCliente = idCliente;
        }

        public BigInteger getCedula() {
              return cedula;
        }

        public void setCedula(BigInteger cedula) {
              this.cedula = cedula;
        }

        public String getNombre() {
               return nombre;
        }

        public void setNombre(String nombre) {
              this.nombre = nombre;
        }

        public String getApellido() {
             return apellido;
        }

        public void setApellido(String apellido) {
              this.apellido = apellido;
        }

        public String getDireccion() {
              return direccion;
        }

        public void setDireccion(String direccion) {
              this.direccion = direccion;
        }

        public Date getFechaNacimiento() {
              return fechaNacimiento;
        }

        public void setFechaNacimiento(Date fechaNacimiento) {
              this.fechaNacimiento = fechaNacimiento;
        }

        public String getTelefono() {
                return telefono;
        }

        public void setTelefono(String telefono) {
               this.telefono = telefono;
        }

        public String getEmail() {
               return email;
        }

        public void setEmail(String email) {
               this.email = email;
        }

        @Override
        public int hashCode() {
                int hash = 0;
                hash += (idCliente != null ? idCliente.hashCode() : 0);
                return hash;
        }

        @Override
        public boolean equals(Object object) {
               if (!(object instanceof Cliente)) {
                   return false;
               }
               Cliente other = (Cliente) object;
               if ((this.idCliente == null && other.idCliente != null) || (this.idCliente != null && !this.idCliente.equals(other.idCliente))) {
                   return false;
                }
                return true;
        }

        @Override
        public String toString() {
               return "modelo.Cliente[ idCliente=" + idCliente + " ]";
        }

}

10.4.- Producto.java

package co.com.microservice.application.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "PRODUCTO", catalog = "", schema = "APPLICATION")
@NamedQueries({
               @NamedQuery(name = "Producto.findAll", query = "SELECT p FROM Producto p"),
               @NamedQuery(name = "Producto.findByIdProducto", query = "SELECT p FROM Producto p WHERE p.idProducto = :idProducto"),
               @NamedQuery(name = "Producto.findByCodigo", query = "SELECT p FROM Producto p WHERE p.codigo = :codigo"),
               @NamedQuery(name = "Producto.findByNombre", query = "SELECT p FROM Producto p WHERE p.nombre = :nombre"),
               @NamedQuery(name = "Producto.findByPrecio", query = "SELECT p FROM Producto p WHERE p.precio = :precio"),
               @NamedQuery(name = "Producto.findByStock", query = "SELECT p FROM Producto p WHERE p.stock = :stock")})
public class Producto implements Serializable {

       private static final long serialVersionUID = 1L;

       @Id
       @Basic(optional = false)
       @NotNull
       @Column(name = "ID_PRODUCTO")
       @SequenceGenerator(name = "sec_producto", sequenceName = "SQ_PRODUCTO", allocationSize = 1)
       @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="sec_producto")
       private BigDecimal idProducto;

       @Basic(optional = false)
       @NotNull
       @Size(min = 1, max = 100)
       @Column(name = "CODIGO")
       private String codigo;

       @Basic(optional = false)
       @NotNull
       @Size(min = 1, max = 100)
       @Column(name = "NOMBRE")
       private String nombre;

       @Basic(optional = false)
       @NotNull
       @Column(name = "PRECIO")
       private BigInteger precio;

       @Basic(optional = false)
       @NotNull
       @Column(name = "STOCK")
       private BigInteger stock;

       public Producto() {
       }

       public Producto(BigDecimal idProducto) {
              this.idProducto = idProducto;
       }

       public Producto(BigDecimal idProducto, String codigo, String nombre, BigInteger precio, BigInteger stock) {
              this.idProducto = idProducto;
              this.codigo = codigo;
              this.nombre = nombre;
              this.precio = precio;
              this.stock = stock;
       }

       public BigDecimal getIdProducto() {
             return idProducto;
       }

       public void setIdProducto(BigDecimal idProducto) {
            this.idProducto = idProducto;
       }

       public String getCodigo() {
           return codigo;
       }

       public void setCodigo(String codigo) {
           this.codigo = codigo;
       }

       public String getNombre() {
           return nombre;
       }

       public void setNombre(String nombre) {
           this.nombre = nombre;
       }

        public BigInteger getPrecio() {
            return precio;
        }

        public void setPrecio(BigInteger precio) {
            this.precio = precio;
        }

        public BigInteger getStock() {
             return stock;
        }

        public void setStock(BigInteger stock) {
            this.stock = stock;
        }

        @Override
        public int hashCode() {
              int hash = 0;
              hash += (idProducto != null ? idProducto.hashCode() : 0);
              return hash;
        }

        @Override
        public boolean equals(Object object) {
               // TODO: Warning - this method won't work in the case the id fields are not set
               if (!(object instanceof Producto)) {
                   return false;
               }
               Producto other = (Producto) object;
               if ((this.idProducto == null && other.idProducto != null) || (this.idProducto != null && !this.idProducto.equals(other.idProducto))) {
                   return false;
               }
               return true;
        }

        @Override
        public String toString() {
              return "modelo.Producto[ idProducto=" + idProducto + " ]";
        }

}

10.5.- OauthClientDetails.java


package co.com.microservice.application.model;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "OAUTH_CLIENT_DETAILS", catalog = "", schema = "APPLICATION")
@NamedQueries({
@NamedQuery(name = "OauthClientDetails.findAll", query = "SELECT o FROM OauthClientDetails o"),
@NamedQuery(name = "OauthClientDetails.findByClientId", query = "SELECT o FROM OauthClientDetails o WHERE o.clientId = :clientId"),
@NamedQuery(name = "OauthClientDetails.findByResourceIds", query = "SELECT o FROM OauthClientDetails o WHERE o.resourceIds = :resourceIds"),
@NamedQuery(name = "OauthClientDetails.findByClientSecret", query = "SELECT o FROM OauthClientDetails o WHERE o.clientSecret = :clientSecret"),
@NamedQuery(name = "OauthClientDetails.findByScope", query = "SELECT o FROM OauthClientDetails o WHERE o.scope = :scope"),
@NamedQuery(name = "OauthClientDetails.findByAuthorizedGrantTypes", query = "SELECT o FROM OauthClientDetails o WHERE o.authorizedGrantTypes = :authorizedGrantTypes"),
@NamedQuery(name = "OauthClientDetails.findByWebServerRedirectUri",  query = "SELECT o FROM OauthClientDetails o WHERE o.webServerRedirectUri = :webServerRedirectUri"),
@NamedQuery(name = "OauthClientDetails.findByAuthorities", query = "SELECT o FROM OauthClientDetails o WHERE o.authorities = :authorities"),
@NamedQuery(name = "OauthClientDetails.findByAccessTokenValidity", query = "SELECT o FROM OauthClientDetails o WHERE o.accessTokenValidity = :accessTokenValidity"),
@NamedQuery(name = "OauthClientDetails.findByRefreshTokenValidity", query = "SELECT o FROM OauthClientDetails o WHERE o.refreshTokenValidity = :refreshTokenValidity"),
@NamedQuery(name = "OauthClientDetails.findByAdditionalInformation", query = "SELECT o FROM OauthClientDetails o WHERE o.additionalInformation = :additionalInformation"),
@NamedQuery(name = "OauthClientDetails.findByAutoapprove", query = "SELECT o FROM OauthClientDetails o WHERE o.autoapprove = :autoapprove")})
public class OauthClientDetails implements Serializable {

       private static final long serialVersionUID = 1L;

       @Id
       @Basic(optional = false)
       @NotNull
       @Size(min = 1, max = 255)
       @Column(name = "CLIENT_ID", nullable = false, length = 255)
       private String clientId;

       @Size(max = 255)
       @Column(name = "RESOURCE_IDS", length = 255)
       private String resourceIds;

       @Size(max = 255)
       @Column(name = "CLIENT_SECRET", length = 255)
       private String clientSecret;

       @Size(max = 255)
       @Column(length = 255)
       private String scope;

       @Size(max = 255)
       @Column(name = "AUTHORIZED_GRANT_TYPES", length = 255)
       private String authorizedGrantTypes;

       @Size(max = 255)
       @Column(name = "WEB_SERVER_REDIRECT_URI", length = 255)
       private String webServerRedirectUri;

       @Size(max = 255)
       @Column(length = 255)
       private String authorities;

       @Column(name = "ACCESS_TOKEN_VALIDITY")
       private Long accessTokenValidity;

       @Column(name = "REFRESH_TOKEN_VALIDITY")
       private Long refreshTokenValidity;

       @Size(max = 2000)
       @Column(name = "ADDITIONAL_INFORMATION", length = 2000)
       private String additionalInformation;

       @Size(max = 255)
       @Column(length = 255)
       private String autoapprove;



        public OauthClientDetails() {
        }



        public OauthClientDetails(String clientId) {
               this.clientId = clientId;
        }

        public String getClientId() {
               return clientId;
        }

        public void setClientId(String clientId) {
               this.clientId = clientId;
        }

        public String getResourceIds() {
               return resourceIds;
        }

        public void setResourceIds(String resourceIds) {
               this.resourceIds = resourceIds;
        }

        public String getClientSecret() {
               return clientSecret;
        }

        public void setClientSecret(String clientSecret) {
               this.clientSecret = clientSecret;
        }

        public String getScope() {
               return scope;
        }

        public void setScope(String scope) {
               this.scope = scope;
        }

        public String getAuthorizedGrantTypes() {
               return authorizedGrantTypes;
        }

        public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
               this.authorizedGrantTypes = authorizedGrantTypes;
        }

        public String getWebServerRedirectUri() {
               return webServerRedirectUri;
        }

        public void setWebServerRedirectUri(String webServerRedirectUri) {
               this.webServerRedirectUri = webServerRedirectUri;
        }

        public String getAuthorities() {
               return authorities;
        }

        public void setAuthorities(String authorities) {
               this.authorities = authorities;
        }

        public Long getAccessTokenValidity() {
               return accessTokenValidity;
        }

        public void setAccessTokenValidity(Long accessTokenValidity) {
               this.accessTokenValidity = accessTokenValidity;
        }

        public Long getRefreshTokenValidity() {
               return refreshTokenValidity;
        }

        public void setRefreshTokenValidity(Long refreshTokenValidity) {
               this.refreshTokenValidity = refreshTokenValidity;
        }

        public String getAdditionalInformation() {
               return additionalInformation;
        }

        public void setAdditionalInformation(String additionalInformation) {
               this.additionalInformation = additionalInformation;
        }

        public String getAutoapprove() {
               return autoapprove;
        }

        public void setAutoapprove(String autoapprove) {
               this.autoapprove = autoapprove;
        }

        @Override
        public int hashCode() {
               int hash = 0;
               hash += (clientId != null ? clientId.hashCode() : 0);
               return hash;
        }

        @Override
        public boolean equals(Object object) {
               // TODO: Warning - this method won't work in the case the id fields are not set
               if (!(object instanceof OauthClientDetails)) {
                   return false;
               }
               OauthClientDetails other = (OauthClientDetails) object;
               if ((this.clientId == null && other.clientId != null) || (this.clientId != null &&                    !this.clientId.equals(other.clientId))) {
                   return false;
               }
               return true;
        }

        @Override
        public String toString() {
               return "modelo.OauthClientDetails[ clientId=" + clientId + " ]";
        }

}

11.- Codificando los DAOs

En primera medida creamos un DAO genérico para que podamos extender sus operaciones según nuestras necesidades:

11.1.- GenericDAO.java


package co.com.microservice.application.repository.generics;

import java.util.List;

public interface GenericDAO<T, ID extends java.io.Serializable> {

    public List<T> getAllRows();

    public T getById(ID id);

    public List<T> getByDescription(String field, String ipParam, String desc);

    public T save(T entity);

    public void delete(ID id);

    public T update(T entity);

    public void flush();

}

11.2.- UserDao.java

package co.com.microservice.application.repository.dao;

import co.com.microservice.application.model.User;
import co.com.microservice.application.repository.generics.GenericDAO;

public interface UserDao extends GenericDAO<User, Long> {

    public boolean isUserAvailable(String login);

    public User loadUserByUsername(String username);

}

11.3.- RoleDao.java

package co.com.microservice.application.repository.dao;

import java.util.List;

import co.com.microservice.application.model.Role;
import co.com.microservice.application.repository.generics.GenericDAO;

public interface RoleDao extends GenericDAO<Role, Long> {

    public List<Role> getRoleListNq();

}

11.4.- ClienteDao.java

package co.com.microservice.application.repository.dao;

import java.math.BigDecimal;
import java.math.BigInteger;

import co.com.microservice.application.model.Cliente;
import co.com.microservice.application.repository.generics.GenericDAO;

public interface ClienteDao extends GenericDAO<Cliente, BigDecimal> {

    public Cliente getClienteByCedula(BigInteger cedula);
    public boolean isExist(Cliente cliente);
    public boolean isExistByCedula(BigInteger cedula);
    public boolean deleteClienteByCedula(BigInteger cedula);
    public List<Cliente> getClientes(Integer offset, Integer limit);

}

11.2.- ProductoDao.java

package co.com.microservice.application.repository.dao;

import java.math.BigDecimal;
import java.util.List;

import co.com.microservice.application.model.Producto;
import co.com.microservice.application.repository.generics.GenericDAO;

public interface ProductoDao extends GenericDAO<Producto, BigDecimal>{

    public List<Producto> getProductos();
    public List<Producto> getByNombre(String nombreProducto);
    public Producto getByCodigo(String codigo);
    public boolean isProductExist(Producto producto);
    public boolean isProductExist(String codigo);
    public boolean deleteByCode(String codigo);

}

11.3.- OAuthClientDAO.java


package co.com.microservice.application.repository.dao;

import co.com.microservice.application.model.OauthClientDetails;
import co.com.microservice.application.repository.generics.GenericDAO;

public interface OAuthClientDAO extends GenericDAO<OauthClientDetails, String>{

       public boolean isClientAvailable(String clientId);
       public OauthClientDetails loadClientById(String clientId);

}

12.- Creando nuestras implementaciones de nuestra capa de Datos

Al igual que con la definición de nuestro DAO genérico, crearemos nuestra implementación genérica de dicho DAO:

12.1.- GenericDAOImpl.java

package co.com.microservice.application.repository.generics;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

public abstract class GenericDAOImpl<T, ID extends java.io.Serializable> implements GenericDAO<T, ID>{

       private Class<T> clazz;
       @Autowired
       private SessionFactory sessionFactory;

       public GenericDAOImpl(Class<T> persistenceClass) {
            this.clazz = persistenceClass;
       }

       @Override
       @Transactional(readOnly = true)
       public List<T> getAllRows() {
             StringBuilder sQuery = new StringBuilder();

             sQuery.append("SELECT t \n");
             sQuery.append("FROM ");
             sQuery.append(getClazz().getSimpleName());
             sQuery.append(" t");

             Query query = getSessionFactory().getCurrentSession().createQuery(sQuery.toString());
 
             List<T> entities = castGenerics(getClazz(), query.list());
 
             return entities;
       }

       @Override
       @Transactional(readOnly = true)
       public T getById(ID id) {

             Object object = getSessionFactory()
                                       .getCurrentSession()
                                       .get(getClazz(), id);
 
             if (getClazz().isInstance(object)){
                  T entity = getClazz().cast(object); 
                  return entity;
             }
 
             return null;
       }

       @Override
       @Transactional(readOnly = true)
       public List<T> getByDescription(String field, String ipParam, String desc) {

              Criteria criteria = getSessionFactory()
                                          .getCurrentSession()
                                          .createCriteria(clazz)
                                          .add(Restrictions.like(ipParam, desc, MatchMode.ANYWHERE));

              List<T> entities = castGenerics(getClazz(), criteria.list());

              return entities;
       }

       @Override
       @Transactional(readOnly = false)
       public T save(T entity) {
              if (entity != null){
                      getSessionFactory()
                            .getCurrentSession()
                            .persist(entity);
                       return entity;
              }
              return null;
       }

       @Override
       @Transactional(readOnly = false)
       public void delete(ID id) {
              try{
                   Object object = getSessionFactory().getCurrentSession().get(getClazz(), id);
 
                   if (getClazz().isInstance(object)){
                       T entity = getClazz().cast(object); 
                       getSessionFactory().getCurrentSession().delete(entity);
                   } 
              }catch (Exception e){
                      e.printStackTrace();
              }
       }

       @Override
       @Transactional(readOnly = false)
       public T update(T entity) {
              if (entity != null){
                     getSessionFactory()
                           .getCurrentSession().update(entity);
                     return entity;
              }
              return null;
       }

       public static <T> List<T> castGenerics(Class<? extends T> clazz, Collection<?> collections) {
              List<T> list = new ArrayList<T>(collections.size());
 
              for(Object object : collections)
                  list.add(clazz.cast(object));
 
              return list;
       }
       @Override
       public void flush() {
       }

       public Class<T> getClazz() {
            return clazz;
       }

       public void setClazz(Class<T> clazz) {
               this.clazz = clazz;
       }

       public SessionFactory getSessionFactory() {
              return sessionFactory;
       }

       public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
       }

}

12.2.- UserDaoImpl.java

package co.com.microservice.application.repository.impl;

import javax.persistence.NoResultException;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import co.com.microservice.application.model.User;
import co.com.microservice.application.repository.dao.UserDao;
import co.com.microservice.application.repository.generics.GenericDAOImpl;

@Repository("userDao")
@Transactional
public class UserDaoImpl extends GenericDAOImpl<User, Long> implements UserDao{

          public UserDaoImpl() {
             super(User.class);
          }

          @Override
          public boolean isUserAvailable(String login) {
               Assert.notNull(login);

               StringBuilder sQuery = new StringBuilder();

               sQuery.append("SELECT count(*) \n");
               sQuery.append("FROM ");
               sQuery.append(getClazz().getSimpleName());
               sQuery.append(" u \n");
               sQuery.append("WHERE u.login = :ipLogin");

               Query query = getSessionFactory().getCurrentSession().createQuery(sQuery.toString());
               query.setParameter("ipLogin", login);

               Long count = (Long) query.list().get(0);

               return count < 1; 
          } 
 
          @Override 
          public User loadUserByUsername(String username) { 
              Assert.notNull(username); 
              User user = null; 
              StringBuilder sQuery = new StringBuilder(); 
 
              sQuery.append("SELECT u \n"); 
              sQuery.append("FROM "); 
              sQuery.append(getClazz().getSimpleName()); 
              sQuery.append(" u \n"); 
              sQuery.append("WHERE u.login = :ipLogin"); 
 
              try{ 
                  user = (User) getSessionFactory()
                                     .getCurrentSession()
                                     .createQuery(sQuery.toString())
                                     .setParameter("ipLogin", username)
                                     .uniqueResult(); 
              }catch (NoResultException nre){ 
                      System.out.println("No se encontro el usuario --> " + nre.toString());
              }

              return user;
          }

}

12.3.- RoleDaoImpl.java

package co.com.microservice.application.repository.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Query;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import co.com.microservice.application.model.Role;
import co.com.microservice.application.repository.dao.RoleDao;
import co.com.microservice.application.repository.generics.GenericDAOImpl;

@Repository("roleDao")
@Transactional
public class RoleDaoImpl extends GenericDAOImpl<Role, Long> implements RoleDao {

        public RoleDaoImpl() {
             super(Role.class);
        }

        @SuppressWarnings({"unchecked" })
        @Override
        public List<Role> getRoleListNq() {

              StringBuilder sQuery = new StringBuilder();

              sQuery.append("SELECT ID_ROLE, \n");
              sQuery.append(" ROLE \n");
              sQuery.append("FROM ROLES");

              Query query = (Query) getSessionFactory().getCurrentSession().createSQLQuery(sQuery.toString()); 

              List<Role> roles = new ArrayList<Role>();
 
              for (Object object : query.getResultList()){
                   if (object instanceof Role){
                       roles.add((Role)object);
                   }
              }
 
              return roles;
        } 

}

12.4.- ClienteDaoImpl.java

package co.com.microservice.application.repository.impl;

import java.math.BigDecimal;
import java.math.BigInteger;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import co.com.microservice.application.model.Cliente;
import co.com.microservice.application.repository.dao.ClienteDao;
import co.com.microservice.application.repository.generics.GenericDAOImpl;

@Repository("clienteDao")
@Transactional
public class ClienteDaoImpl extends GenericDAOImpl<Cliente, BigDecimal> implements ClienteDao {

          public ClienteDaoImpl() {
                 super(Cliente.class);
          }

          @Override
          public Cliente getClienteByCedula(BigInteger cedula) {
                 try {
                      if (cedula != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Cliente.findByCedula");
                          query.setBigInteger("cedula", cedula);

                          Cliente cliente = null;

                          if (query.list().size() > 0){
                              cliente = (Cliente) query.list().get(0);
                          }
                          return cliente;
                      }
                 } catch (Exception e) {
                          e.printStackTrace();
                 }

                 return null;
          }

          @Override
          public boolean isExist(Cliente cliente) {

                  try {
                      if (cliente != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Cliente.findByCedula");
                          query.setBigInteger("cedula", cliente.getCedula());

                          int numClientes = query.list().size();

                          if (numClientes != 0)
                              return true;
                      }
                  } catch (Exception e) {
                            e.printStackTrace();
                  }

                   return false;
          }

          @Override
          public boolean isExistByCedula(BigInteger cedula) {
                 try {
                      if (cedula != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Cliente.findByCedula");
                          query.setBigInteger("cedula", cedula);

                          int numClientes = query.list().size();

                          if (numClientes != 0)
                              return true;
                      }
                 } catch (Exception e) {
                         e.printStackTrace();
                 } 

                 return false;
          }

          @Override
          public boolean deleteClienteByCedula(BigInteger cedula) {
                 try {
                      if (cedula != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Cliente.findByCedula");
                          query.setBigInteger("cedula", cedula);

                          if (query.list().size() > 0){
                              Cliente cliente = (Cliente) query.list().get(0);
                              getSessionFactory().getCurrentSession().delete(cliente);

                              return true;
                          }

                      }
                 } catch (Exception e) {
                          e.printStackTrace();
                 }

                 return false;
          }

          @Override
          public List<Cliente> getClientes(Integer offset, Integer limit) {
                 try{
                     Query query = getSessionFactory().getCurrentSession().getNamedQuery("Cliente.findAll");
 
                     query.setFirstResult(offset);
                     query.setMaxResults(limit);
 
                    if (query.list().size() > 0) {
 
                        List<Cliente> list = new ArrayList<Cliente>();
 
                        for (Object cliente : query.list()){
                             if (cliente instanceof Cliente){
                                 list.add((Cliente)cliente);
                             }
                        } 
 
                        return list;
 
                    }
 
               }catch(Exception e){
                      e.printStackTrace();
               }
 
              return null;
       }

}

12.5.- ProductoDaoImpl.java

package co.com.microservice.application.repository.impl;

import java.math.BigDecimal;
import java.util.List;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import co.com.microservice.application.model.Producto;
import co.com.microservice.application.repository.dao.ProductoDao;
import co.com.microservice.application.repository.generics.GenericDAOImpl;

@Repository("productoDao")
@Transactional
public class ProductoDaoImpl extends GenericDAOImpl<Producto, BigDecimal> implements ProductoDao {

          public ProductoDaoImpl(){
               super(Producto.class);
          }

          @Override
          public List<Producto> getProductos() {
                 Query query = getSessionFactory().getCurrentSession().getNamedQuery("Producto.findAll");

                 List<Producto> productos = new ArrayList<Producto>();
 
                 for (Object producto : query.list()){
                      if (producto instanceof Producto){
                          productos.add((Producto)producto);
                      }
                 }
 
                 return productos;
          }

          @Override
          public List<Producto> getByNombre(String nombreProducto) {

                StringBuilder sbQuery = new StringBuilder();
                StringBuilder sbParam = new StringBuilder();

                sbQuery.append("SELECT p \n");
                sbQuery.append("FROM Producto p \n");
                sbQuery.append("WHERE p.nombre LIKE :nomProducto \n");
                sbQuery.append("ORDER BY p.nombre");

                sbParam.append("%");
                sbParam.append(nombreProducto.toUpperCase());
                sbParam.append("%");

                Query query = getSessionFactory()
                                  .getCurrentSession()
                                  .createQuery(sbQuery.toString());
                query.setString("nomProducto", sbParam.toString());

                List<Producto> productos = new ArrayList<Producto>();
 
                for (Object producto : query.list()){
                     if (producto instanceof Producto){
                         productos.add((Producto)producto);
                     }
                }
 
                return productos;
          }

          @Override
          public Producto getByCodigo(String codigo) {
                StringBuilder sbQuery = new StringBuilder();

                sbQuery.append("SELECT p \n");
                sbQuery.append("FROM Producto p \n");
                sbQuery.append("WHERE p.codigo = :codigo");

                Query query = getSessionFactory()
                                       .getCurrentSession()
                                       .createQuery(sbQuery.toString());
                query.setString("codigo", codigo);

                Producto producto = null;

                if (query.list().size() > 0){
                    producto = (Producto) query.list().get(0);
                }

                return producto;
          }

          @Override
          public boolean isProductExist(Producto producto) {

                 try {
                      if (producto != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Producto.findByCodigo");
                          query.setString("codigo", producto.getCodigo());
                          int numProdcuts = query.list().size();
                          if (numProdcuts != 0)
                               return true;
                          }
                 } catch (Exception e) {
                          e.printStackTrace();
                 }

                 return false;
          } 

          @Override
          public boolean isProductExist(String codigo) {
                 try {
                      if (codigo != null){
                          Query query = getSessionFactory().getCurrentSession().getNamedQuery("Producto.findByCodigo");
                          query.setString("codigo", codigo);
                          int numProdcuts = query.list().size();
                          if (numProdcuts != 0)
                              return true;
                      }
                  } catch (Exception e) {
                           e.printStackTrace();
                  }
                   return false;
          }

          @Override
          public boolean deleteByCode(String codigo) {
                  try {
                        if (codigo != null){
                            Query query = getSessionFactory().getCurrentSession().getNamedQuery("Producto.findByCodigo");
                            query.setString("codigo", codigo);
                            if (query.list().size() > 0){
                                Producto producto = (Producto) query.list().get(0);
                                getSessionFactory().getCurrentSession().delete(producto);
                                return true;
                            } 
                        }
                  } catch (Exception e) {
                           e.printStackTrace();
                  }
                  return false;
          }

}

12.6.- OAuthClientDAOImpl.java


package co.com.microservice.application.repository.impl;

import javax.persistence.NoResultException;

import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import co.com.microservice.application.model.OauthClientDetails;
import co.com.microservice.application.repository.dao.OAuthClientDAO;
import co.com.microservice.application.repository.generics.GenericDAOImpl;

@Transactional
@Repository("OAuthClientDAO")
public class OAuthClientDAOImpl extends GenericDAOImpl<OauthClientDetails, String> implements OAuthClientDAO {

       public OAuthClientDAOImpl() {
               super(OauthClientDetails.class);
       }

       @Override
       public boolean isClientAvailable(String clientId) {
              Assert.notNull(clientId);

              StringBuilder sQuery = new StringBuilder();

              sQuery.append("SELECT count(*) \n");
              sQuery.append("FROM ");
              sQuery.append(getClazz().getSimpleName());
              sQuery.append(" c \n");
              sQuery.append("WHERE c.clientId = :ipClientId");

              Query query = getSessionFactory().getCurrentSession().createQuery(sQuery.toString());
              query.setParameter("ipClientId", clientId);

              Long count = (Long) query.list().get(0);

              return count >= 1 ? true : false;
       }

       @Override
       public OauthClientDetails loadClientById(String clientId) {
              Assert.notNull(clientId);

              OauthClientDetails client = null;

              StringBuilder sQuery = new StringBuilder();

              sQuery.append("SELECT c \n");
              sQuery.append("FROM ");
              sQuery.append(getClazz().getSimpleName());
              sQuery.append(" c \n");
              sQuery.append("WHERE c.clientId = :ipClientId");


              try{
                   client = (OauthClientDetails) getSessionFactory().getCurrentSession()
                                                         .createQuery(sQuery.toString())
                                                         .setParameter("ipClientId", clientId)
                                                         .uniqueResult();
              }catch (NoResultException nre){
                      System.out.println("No se encontro el cliente --> " + nre.toString());
              }

              return client;
        }

}

13.- Definición de la capa de servicios

13.1.- UserServicesDao.java

package co.com.microservice.application.service.security.dao;

import java.util.List;

import co.com.microservice.application.model.Role;
import co.com.microservice.application.model.User;

public interface UserServicesDao {

    public void createUser(User u, List<Role> role);

    public boolean isUserAvailable(String username);

    public User getUserByLogin(String login);

    public User actualizar(User u);

    public void eliminar(User u);

}

13.2.- RoleServicesDao.java

package co.com.microservice.application.service.security.dao;

import java.util.List;

import co.com.microservice.application.model.Role;

public interface RoleServicesDao {

    public List<Role> getAllRoles();

    public Role getInfoRole();

    public void deleteRole();

    public void updateRole();

}

13.3.- ClienteServicesDao.java

package co.com.microservice.application.service.dao;

import java.math.BigInteger;
import java.util.List;

import co.com.microservice.application.model.Cliente;

public interface ClienteServicesDao {

    public void createCliente(Cliente client);
    public List<Cliente> getAllClientes();
    public Cliente getInfoCliente(BigInteger cedula);
    public void deleteCliente(Cliente client);
    public boolean deleteCliente(BigInteger cedula);
    public void updateCliente(Cliente client);
    public boolean isClientExist(Cliente client);
    public boolean isClientExist(BigInteger cedula);
    public List<Cliente> getAllClientes(Integer offset, Integer limit); 

}

13.4.- ProductoServicesDao.java

package co.com.microservice.application.service.dao;

import java.util.List;

import co.com.microservice.application.model.Producto;

public interface ProductoServicesDao {

    public void createProduct(Producto producto);
    public List<Producto> getAllProducts();
    public Producto getInfoProduct(String codigo);
    public void deleteProduct(Producto producto);
    public void updateProduct(Producto producto);
    public List&lt;Producto&gt; getProductsByName(String nombre);
    public boolean isProductExist(Producto producto);
    public boolean isProductExist(String codigo);
    public boolean deleteProductByCode(String codigo);

}

13.5.- ClientServicesDao.java


package co.com.microservice.application.service.security.dao;

import co.com.microservice.application.model.OauthClientDetails;

public interface ClientServicesDao {

       public void createClient(OauthClientDetails client);

       public boolean isUserAvailable(String clientId);

       public OauthClientDetails getClientById(String clientId);

       public OauthClientDetails actualizar(OauthClientDetails client);

       public void eliminar(OauthClientDetails client);

}

14.- Implementación de los DAOs de la capa de servicio

14.1.- CustomUserDetailServices.java

Esta es una clase importante para Spring Security ya que esta se encarga de crear el objeto USER durante el tiempo de ejecución, y que ademas contiene la información de seguridad.

package co.com.microservice.application.service.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import co.com.microservice.application.model.Role;
import co.com.microservice.application.repository.dao.UserDao;
import co.com.microservice.application.service.security.dao.UserServicesDao;

@Service("customUserDetailsService")
@Transactional
public class CustomUserDetailServices implements UserDetailsService, UserServicesDao {

       @Autowired
       private UserDao userDao;

       @Autowired
       private PasswordEncoder passwordEncoder;

       @Override
       public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

              co.com.microservice.application.model.User user = userDao.loadUserByUsername(username);

              if (user == null)
                  throw new UsernameNotFoundException("No se encontro informacion del usuario!");

              return new User(user.getLogin(),
                              user.getPassword(),
                              Boolean.valueOf(user.getEnable()),
                              Boolean.valueOf(user.getAccountNonExpired()),
                              Boolean.valueOf(user.getCredentialNonExpired()),
                              Boolean.valueOf(user.getAccountNonLocked()),
                              getAuthorities(user.getRoleList())
                              );
       }

       public Collection<? extends GrantedAuthority> getAuthorities(List<Role> role) {
               List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>(2);

               Iterator<Role> iterRole = role.iterator();

               while (iterRole.hasNext()) {
                      Role rol = iterRole.next();
                      authoritiesList.add(new SimpleGrantedAuthority(rol.getRole()));
                      System.out.println("ROLES --> " + rol.getRole());
               }

               return authoritiesList;
       }

       @Override
       public boolean isUserAvailable(String username) {

             boolean available = userDao.isUserAvailable(username);

             return available;
       }

       @Override
       public co.com.microservice.application.model.User getUserByLogin(String login) {
              return userDao.loadUserByUsername(login);
       }

       @Override
       public co.com.microservice.application.model.User actualizar(co.com.microservice.application.model.User u) {
               userDao.update(u);
               return u;
       }

       @Override
       public void eliminar(co.com.microservice.application.model.User u) {
             userDao.delete(u.getIdUser().longValue());
       }

       @Override
       public void createUser(co.com.microservice.application.model.User u, List<Role> role) {
              u.setPassword(passwordEncoder.encode(u.getPassword()));
              u.setRoleList(role);
              userDao.save(u);
       }

}

14.2.- RoleServiceDaoImpl.java

package co.com.microservice.application.service.security;

import org.springframework.stereotype.Service;

@Service("roleServices")
public class RoleServiceDaoImpl {

}

14.3.- ClienteServicesDaoImpl.java

package co.com.microservice.application.service.dao.impl;

import java.math.BigInteger;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import co.com.microservice.application.model.Cliente;
import co.com.microservice.application.repository.dao.ClienteDao;
import co.com.microservice.application.service.dao.ClienteServicesDao;

@Service("clienteServices")
public class ClienteServicesDaoImpl implements ClienteServicesDao {

    @Autowired
    private ClienteDao clienteDao;

     @Override
     public void createCliente(Cliente client) {
        try{
                 if (client != null){
                     clienteDao.save(client);
                 }
        }catch (Exception e){
          e.printStackTrace();
        }
    }

    @Override
    public List<Cliente> getAllClientes() {
         try{
            return clienteDao.getAllRows();
         }catch(Exception e){
             System.out.println("Error en el servicio Cliente --> " + e.toString());
         }
          return null;
    }

    @Override
    public Cliente getInfoCliente(BigInteger cedula) {
         if (cedula != null){
            System.out.println("2. CEDULA --> " + cedula);
            return clienteDao.getClienteByCedula(cedula);
         }

         return null;
    }
    @Override
    public boolean deleteCliente(BigInteger cedula) {
           if (cedula != null){
               clienteDao.deleteClienteByCedula(cedula);
               return true;
           } 

           return false;
    }

    @Override
    public void updateCliente(Cliente client) {
           if (client != null){
               clienteDao.update(client);
           }
    }

    @Override
    public boolean isClientExist(Cliente client) {
           return clienteDao.isExist(client);
    }

    @Override
    public boolean isClientExist(BigInteger cedula) {
           return clienteDao.isExistByCedula(cedula);
    }

    @Override
    public void deleteCliente(Cliente client) {
           if (client != null){
               clienteDao.delete(client.getIdCliente());
           }
    }

    @Override
    public List<Cliente> getAllClientes(Integer offset, Integer limit) {
 
            if (offset != null && limit != null){
                return clienteDao.getClientes(offset, limit);
            }
 
            return null;
    }

}

14.4.- ProductoServicesDaoImpl.java

package co.com.microservice.application.service.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import co.com.microservice.application.model.Producto;
import co.com.microservice.application.repository.dao.ProductoDao;
import co.com.microservice.application.service.dao.ProductoServicesDao;

@Service("productoServices")
public class ProductoServicesDaoImpl implements ProductoServicesDao {

       @Autowired
       private ProductoDao productoDao;

       @Override
       public void createProduct(Producto producto) {
              try {
                   if (producto != null){
                       productoDao.save(producto);
                   }
              } catch (Exception e) {
                       e.printStackTrace();
              }
       }

       @Override
       public List<Producto> getAllProducts() {
              try {
                   return productoDao.getAllRows();
              } catch (Exception e) {
                       e.printStackTrace();
              }
              return null;
       }

       @Override
       public Producto getInfoProduct(String codigo) {
              try {
                   Producto producto = productoDao.getByCodigo(codigo);
                   return producto;
              } catch (Exception e) {
                       e.printStackTrace();
              }
              return null;
       }

       @Override
       public void deleteProduct(Producto producto) {
             try {
                  if (producto != null){
                      productoDao.delete(producto.getIdProducto());
                  }
             } catch (Exception e) {
                       e.printStackTrace();
             }
       }

       @Override
       public void updateProduct(Producto producto) {
              try {
                   if (producto != null){
                       productoDao.update(producto);
                   }
              } catch (Exception e) {
                       e.printStackTrace();
              }

       }

       @Override
       public List<Producto> getProductsByName(String nombre) {
              try {
                   if (nombre != null){
                       System.out.println("1. NOMBRE --> " + nombre);
                       List<Producto> productos = (List<Producto>) productoDao.getByNombre(nombre);
                       return productos;
                   }
              } catch (Exception e) {
                       e.printStackTrace();
              }
              return null;
       }

       @Override
       public boolean isProductExist(Producto producto) {
              return productoDao.isProductExist(producto);
       }

       @Override
       public boolean isProductExist(String codigo) {
              return productoDao.isProductExist(codigo);
       }

       @Override
       public boolean deleteProductByCode(String codigo) {
              if (codigo != null){
                  productoDao.deleteByCode(codigo);
                  return true;
              }
              return false;
       }

}

14.5.- CustomClientDetailService.java


package co.com.microservice.application.service.security;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import co.com.microservice.application.model.OauthClientDetails;
import co.com.microservice.application.repository.dao.OAuthClientDAO;
import co.com.microservice.application.service.security.dao.ClientServicesDao;
import co.com.microservice.application.utils.Constantes;

@Service("customClientDetailsService")
@Transactional
public class CustomClientDetailService implements ClientDetailsService, ClientServicesDao {

       @Autowired
       private OAuthClientDAO oauthClientDAO;

       @Override
       public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {

              if (!oauthClientDAO.isClientAvailable(clientId)){
                  throw new ClientRegistrationException(String.format(Constantes.MSG_ERROR_CLIENTE_NO_REGISTRADO, clientId));
              }

              OauthClientDetails client = oauthClientDAO.loadClientById(clientId);

              BaseClientDetails clientDetails = new BaseClientDetails();

              List<String> authorities = Arrays.asList(client.getAuthorizedGrantTypes().split(","));

              List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();

              for (String s : authorities){
                   authoritiesList.add(new SimpleGrantedAuthority(s));
              }

              Set<String> uris = new HashSet<String>(Arrays.asList(client.getWebServerRedirectUri().split(",")));

              clientDetails.setClientId(client.getClientId());
              clientDetails.setScope(Arrays.asList(client.getScope().split(",")));
              clientDetails.setAuthorizedGrantTypes(Arrays.asList(client.getAuthorizedGrantTypes().split(",")));
              clientDetails.setAuthorities(authoritiesList);
              clientDetails.setAccessTokenValiditySeconds(client.getAccessTokenValidity().intValue());
              clientDetails.setClientSecret(client.getClientSecret());
              clientDetails.setRegisteredRedirectUri(uris);
              clientDetails.setResourceIds(Arrays.asList(client.getResourceIds().split(",")));
              //clientDetails.setAdditionalInformation();

              if(client.getAutoapprove().equalsIgnoreCase("true"))
                 clientDetails.setAutoApproveScopes(StringUtils.commaDelimitedListToSet(client.getAutoapprove()));
              else
                  clientDetails.setAutoApproveScopes(new HashSet<String>());

              return clientDetails;
       }

       @Override
       public void createClient(OauthClientDetails client) {
              if (client != null){
                  oauthClientDAO.save(client);
              }
       }

       @Override
       public boolean isUserAvailable(String clientId) {
              if (clientId != null){
                  oauthClientDAO.getById(clientId);
              }

              return false;
       }

       @Override
       public OauthClientDetails getClientById(String clientId) {

              OauthClientDetails client = null;

              if (!clientId.equalsIgnoreCase("")){
                  client = oauthClientDAO.getById(clientId);
              }

              return client;
       }

       @Override
       public OauthClientDetails actualizar(OauthClientDetails client) {

              if (client != null){
                  oauthClientDAO.update(client);
              }

              return client;
       }

       @Override
       public void eliminar(OauthClientDetails client) {
              if (client != null){
                  oauthClientDAO.delete(client.getClientId());
              }
       }

}

15.- Definición del Servicio Rest

15.1.- RestProductoServices.java

package co.com.microservice.application.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import co.com.microservice.application.model.Producto;
import co.com.microservice.application.service.dao.ProductoServicesDao;
import co.com.microservice.application.servicesaux.InfoResponse;
import co.com.microservice.application.servicesaux.InformacionProductoRs;
import co.com.microservice.application.servicesaux.InformacionProductosRs;
import co.com.microservice.application.servicesaux.InformacionRs;

@RestController
@RequestMapping(value = "/api/v1.0")
public class RestProductoServices {

       @Autowired
       private ProductoServicesDao productoServicesDao;

       //------------------- Create Producto --------------------------------------------------------

       @RequestMapping(value = "/productos",
                       method = RequestMethod.POST,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Void> crearProducto(@RequestBody(required = true) Producto producto, UriComponentsBuilder ucBuilder){

                  if (productoServicesDao.isProductExist(producto)){
                       return new ResponseEntity<Void>(HttpStatus.CONFLICT);
                  }

                  productoServicesDao.createProduct(producto);

                  HttpHeaders header = new HttpHeaders();
                  header.setLocation(ucBuilder.path("/productos/{id}").buildAndExpand(producto.getIdProducto()).toUri());

                  return new ResponseEntity<Void>(header, HttpStatus.OK);
       }

       //------------------- List Producto --------------------------------------------------------

       @RequestMapping(value = "/productos",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<List<Producto>> listadoProductos(){

               List<Producto> productos = productoServicesDao.getAllProducts();

               if (productos.size() == 0){
                   return new ResponseEntity<List<Producto>>(HttpStatus.NOT_FOUND);
               }

               return new ResponseEntity<List<Producto>>(productos, HttpStatus.OK);
       }

       //------------------- List Producto Paginacion --------------------------------------------------------
 
       @RequestMapping(value = "/productos/paginacion",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE, 
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<List<Producto>> listadoProductos(@RequestParam(value = "start", required = true) int start, 
                                                              @RequestParam(value = "size", required = true) int size){
 
              if (start == 0 || size == 0){
                  return new ResponseEntity<List<Producto>>(HttpStatus.BAD_REQUEST);
              }
 
              List<Producto> productos = productoServicesDao.getAllProducts();
 
              if (productos.size() == 0){
                  return new ResponseEntity<List<Producto>>(HttpStatus.NOT_FOUND);
              }
 
              if (start + size > productos.size()){
                  return new ResponseEntity<List<Producto>>(HttpStatus.PAYLOAD_TOO_LARGE);
              }
 
              return new ResponseEntity<List<Producto>>(productos.subList(start, start + size), HttpStatus.OK); 
       }
       //------------------- List Producto --------------------------------------------------------

       @RequestMapping(value = "/productos/{nombre}",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<List<Producto>> listadoProductosPorNombre(@PathVariable("nombre") String nombre){

                 if (nombre.equalsIgnoreCase("") || nombre == null){
                      return new ResponseEntity<List<Producto>>(HttpStatus.NO_CONTENT);
                 }

                 List<Producto> productos = productoServicesDao.getProductsByName(nombre);

                 if (productos.size() == 0){
                      return new ResponseEntity<List<Producto>>(HttpStatus.NOT_FOUND);
                 }

                 return new ResponseEntity<List<Producto>>(productos, HttpStatus.OK);
       }

       //------------------- Info Producto --------------------------------------------------------

       @RequestMapping(value = "/productos/{codigo}",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Producto> informacionProducto(@PathVariable("codigo") String codigo){
 
                if (codigo.equalsIgnoreCase("") || codigo == null){
                    return new ResponseEntity<Producto>(HttpStatus.NO_CONTENT);
                }

                Producto producto = (Producto) productoServicesDao.getInfoProduct(codigo);

                if (producto == null){
                    return new ResponseEntity<Producto>(HttpStatus.NOT_FOUND);
                }

                return new ResponseEntity<Producto>(producto, HttpStatus.OK);
       }

       //------------------- Delete Producto --------------------------------------------------------

       @RequestMapping(value = "/productos/{codigo}",
                       method = RequestMethod.DELETE,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Producto> eliminarProducto(@PathVariable("codigo") String codigo){

              if (codigo.equalsIgnoreCase("") || codigo == null){
                  return new ResponseEntity<Producto>(HttpStatus.NO_CONTENT);
              }

              if (!productoServicesDao.isProductExist(codigo)){
                  return new ResponseEntity<Producto>(HttpStatus.NOT_FOUND);
              }

              boolean exito = productoServicesDao.deleteProductByCode(codigo);

              if (!exito){
                   return new ResponseEntity<Producto>(HttpStatus.NOT_MODIFIED);
              }

              return new ResponseEntity<Producto>(HttpStatus.OK);
       }

       //------------------- Update Producto --------------------------------------------------------

       @RequestMapping(value = "/productos/{codigo}",
                       method = RequestMethod.PUT,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Producto> actualizarProducto(@PathVariable("codigo") String codigo, @RequestBody(required = true) Producto producto){

               if (!productoServicesDao.isProductExist(codigo)){
                    return new ResponseEntity<Producto>(HttpStatus.NOT_FOUND);
               }

               productoServicesDao.updateProduct(producto);

               return new ResponseEntity<Producto>(producto, HttpStatus.NOT_FOUND);
       }

}

15.2.- RestClienteServices.java

package co.com.microservice.application.controller;

import java.math.BigInteger;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import co.com.microservice.application.model.Cliente;
import co.com.microservice.application.service.dao.ClienteServicesDao;
import co.com.microservice.application.servicesaux.InfoResponse;
import co.com.microservice.application.servicesaux.InformacionClienteRs;
import co.com.microservice.application.servicesaux.InformacionClientesRs;
import co.com.microservice.application.servicesaux.InformacionRs;

@RestController
@RequestMapping(value = "/api/v1.0")
public class RestClienteServices {

       @Autowired
       private ClienteServicesDao clienteServicesDao;

       //------------------- Get All Clientes --------------------------------------------------------

       @RequestMapping(value = "/clientes",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<List<Cliente>> obtenerClientes() {

              List<Cliente> clientes = clienteServicesDao.getAllClientes();

              if (clientes.isEmpty()){
                  return new ResponseEntity<List<Cliente>>(HttpStatus.NO_CONTENT);
              }

              return new ResponseEntity<List<Cliente>>(clientes, HttpStatus.OK);
       }

       //------------------- Get All Clientes Paginacion --------------------------------------------------------
 
       @RequestMapping(value = "/clientes/paginacion",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE, 
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<List<Cliente>> obtenerClientesPaginacion(@RequestParam(value = "offset", required = true) Integer offset, 
                                                                      @RequestParam(value = "limit", required = true) Integer limit) {
 
              if (offset == null || limit == null){
                  return new ResponseEntity<List<Cliente>>(HttpStatus.BAD_REQUEST);
              }
 
              List<Cliente> clientes = clienteServicesDao.getAllClientes(offset, limit);
 
              if (clientes.isEmpty()){
                  return new ResponseEntity<List<Cliente>>(HttpStatus.NO_CONTENT);
              }
 
              return new ResponseEntity<List<Cliente>>(clientes, HttpStatus.OK); 
       }

       //------------------- Get Cliente by cedula --------------------------------------------------------

       @RequestMapping(value = "/clientes/{cedula}",
                       method = RequestMethod.GET,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Cliente> obtenerInfoCliente(@PathVariable("cedula") BigInteger cedula) {

              Cliente cliente = clienteServicesDao.getInfoCliente(cedula);

              if (cliente == null){
                   return new ResponseEntity<Cliente>(HttpStatus.NOT_FOUND);
              }

              return new ResponseEntity<Cliente>(cliente, HttpStatus.OK);
       }

       //------------------- Create Cliente --------------------------------------------------------

       @RequestMapping(value = "/clientes",
                       method = RequestMethod.POST,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Void> crearCliente(@RequestBody(required = true) Cliente cliente, UriComponentsBuilder ucBuilder) {

              if (clienteServicesDao.isClientExist(cliente)){
                  return new ResponseEntity<Void>(HttpStatus.CONFLICT);
              }

              clienteServicesDao.createCliente(cliente);

              HttpHeaders header = new HttpHeaders();
              header.setLocation(ucBuilder.path("/clientes/{id}").buildAndExpand(cliente.getIdCliente()).toUri());

              return new ResponseEntity<Void>(header, HttpStatus.CREATED);
       }

       //------------------- Delete Cliente --------------------------------------------------------

       @RequestMapping(value = "/clientes/{cedula}",
                       method = RequestMethod.DELETE,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Cliente> eliminarCliente(@PathVariable("cedula") BigInteger cedula) {

              if (!clienteServicesDao.isClientExist(cedula)){
                  return new ResponseEntity<Cliente>(HttpStatus.NOT_FOUND);
              }

              boolean exito = clienteServicesDao.deleteCliente(cedula);

              if (!exito){
                  return new ResponseEntity<Cliente>(HttpStatus.NOT_MODIFIED);
              }

              return new ResponseEntity<Cliente>(HttpStatus.OK);
       }

       @RequestMapping(value = "/clientes/{cedula}",
                       method = RequestMethod.PUT,
                       headers = "Accept=application/json",
                       consumes = MediaType.APPLICATION_JSON_VALUE,
                       produces = "application/json; charset=UTF-8")
       @ResponseBody
       public ResponseEntity<Cliente> actualizarCliente(@PathVariable("cedula") BigInteger cedula,@RequestBody(required = true) Cliente client) {

              if (!clienteServicesDao.isClientExist(cedula)){
                  return new ResponseEntity<Cliente>(HttpStatus.NOT_FOUND);
              }

              clienteServicesDao.updateCliente(client);

              return new ResponseEntity<Cliente>(client, HttpStatus.OK);
       } 

}

15.3.- RestServices.java

package co.com.microservice.application.controller;

import java.util.Date;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import co.com.microservice.application.servicesaux.InfoResponse;
import co.com.microservice.application.servicesaux.InputRq;
import co.com.microservice.application.servicesaux.OutputRs;

@RestController
@RequestMapping(value = "/api/v1.0")
public class RestServices {

        @RequestMapping(value = "/mensajes/{nombre}",
                        method = RequestMethod.GET,
                        headers = "Accept=application/json",
                        consumes = MediaType.APPLICATION_JSON_VALUE,
                        produces = "application/json; charset=UTF-8")
        @ResponseBody
        public ResponseEntity<String> getSaludo(@PathVariable("nombre") String nombre) {

                if (nombre.equalsIgnoreCase("") || nombre == null){
                    return new ResponseEntity<String>(HttpStatus.OK);
                }

                StringBuffer sb = new StringBuffer();

                sb.append("Hola. ");
                sb.append(nombre);
                sb.append(", mucho gusto \n ");
                sb.append(new Date());

                return new ResponseEntity<String>(sb.toString(), HttpStatus.OK);
        }

        @RequestMapping(value = "/seguro/mensajes/{nombre}",
                        method = RequestMethod.GET,
                        headers = "Accept=application/json",
                        consumes = MediaType.APPLICATION_JSON_VALUE,
                        produces = "application/json; charset=UTF-8")
        @ResponseBody
        public ResponseEntity<String> getSaludoAdmin(@PathVariable("nombre") String nombre) {
               if (nombre.equalsIgnoreCase("") || nombre == null){
                   return new ResponseEntity<String>(HttpStatus.OK);
               }

               StringBuffer sb = new StringBuffer();

               sb.append("Hola. ");
               sb.append(nombre);
               sb.append(", mucho gusto \n ");
               sb.append(new Date());

               return new ResponseEntity<String>(sb.toString(), HttpStatus.OK);
        }

}

16.- Programación Orientada a Aspectos(AOP)

Este es un concepto que me encontré hace mucho tiempo y desde entonces lo aplico a mis proyectos para comprenderlo mejor se puede referir a estas páginas muy buenas por cierto dejan bien claro el concepto esta de arquitecturajava o esta de javamexico  donde el usuario ezamudio explica una forma elegante a mi parecer y que he decido implementar también la AOP:

16.1.- Creando una Anotación Personalizada

El propósito de crear esta anotación es que nos permitirá anotar todos aquellos métodos a los que queramos ver vía Log su información de entrada y salida:


package co.com.microservice.application.anotaciones;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InfoLogger {

       String origen();

}

16.2.- Creando Nuestro Aspecto

Para crear un aspecto nos basta con crear un clase en nuestro caso LogginAspect y anotarla con @Aspect del paquete org.aspectj.lang.annotation.Aspect ya dentro del paquete vamos a tener nuestros PointCuts osea aquellos puntos de nuestra aplicación donde aplicaremos nuestros  Advices(Before,After,AfterThrowing,Around,AfterReturning) que no son otra cosa que la implementación de nuestro aspecto osea que es lo que se hace cuando un PointCut activa el aspecto, con un ejemplo creo que se clarifica aún mas, ademas existe todo un EL(Expression Language) para definir nuestros PointCuts: es probable que nos encontremos con cosas como @Pointcut(“execution(public * co.com.microservice.application.controller.*.obtener*())”) esto queire decir que la implementación de Aspecto osea sus Advices se ejecute para todos aquellos métodos que esten en todas las clases del paquete co.com.microservice.application.controller cuyos métodos seán públicos devuelvan cualquier tipo y no reciban parámetros pueden consultar la guía de spring para entender mejor, para nuestro caso como vamos a usar una anotación creamos el siguiente pointcut @Pointcut(“@annotation(co.com.microservice.application.anotaciones.InfoLogger)”):


package co.com.microservice.application.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Aspect
@Component
public class LogginAspect {

       private static final Logger logger = LoggerFactory.getLogger(LogginAspect.class);

       /**
        * <p>Metodo: Definicion del PointCut controller.</p>
        * <p>Descripcion: Este metodo es la definicion del pointcut para identificar todos los                metodos anotados con @InfoLogger.</p>
        */
        @Pointcut("@annotation(co.com.microservice.application.anotaciones.InfoLogger)")
        public void infoLoggerPointcut() {}

      /**
       * <p>Metodo: Definicion del PointCut controller.</p>
       * <p>Descripcion: Este metodo es la definicion del pointcut para identificar todas las          clases anotadas con @Controller.</p>
       */
      //@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
      //public void controllerPointcut() {}

     /**
      * <p>Metodo: Definicion del PointCut requestMapping.</p>
      * <p>Descripcion: Este metodo es la definicion del pointcut para identificar todas las clases anotadas con @RequestMapping.</p>
      */
    //@Pointcut("within(@org.springframework.web.bind.annotation.RequestMapping *)")
    //public void requestMappingPointcut() {}

    /**
     * <p>Metodo: Definicion del PointCut.</p>
     * <p>Descripcion: Este metodo es la definicion del pointcut para la ejecución de todos lo metodos DAOImpl y que comienzan por get.</p>
     */
    //@Pointcut("execution(public * co.com.microservice.application.controller.*.obtener*())")
    //public void operacionesGetPointcut(){};

   /**
    * <p>Metodo: Antes de la Operacion.</p>
    * <p>Descripcion: Este metodo se ejecuta antes de cada metodo.</p>
    * @param jp de tipo JoinPoint con la informacion de Objetos de entrada y salida del metodo
    */
  //@Before("infoLoggerPointcut() && controllerPointcut() && requestMappingPointcut()  && operacionesGetPointcut()")
   @Before("infoLoggerPointcut()")
   public void beforeOper(JoinPoint jp){
         // Impresion en el log
         try{
             Object result = jp.getArgs();
             ObjectMapper objectMapper = new ObjectMapper();
             logger.debug("*============================ BEFORE > > > {}", objectMapper.writeValueAsString(result));
         }catch (JsonProcessingException jpe){
                 logger.error("JsonGenerationException > > > {}", jpe.toString());
         }catch (Throwable e){
                 logger.error("*============================ Throwable > > > {}", e.toString());
         }
    }

    /**
     * <p>Metodo: Despues de la Operacion.</p>
     * <p>Descripcion: Este metodo se ejecuta antes de cada metodo.</p>
     * @param jp de tipo JoinPoint con la informacion de Objetos de entrada y salida del metodo
     */ 
    //@After("infoLoggerPointcut() && controllerPointcut() && requestMappingPointcut() && operacionesGetPointcut()")
     @After("infoLoggerPointcut()")
     public void afterOper(JoinPoint jp){
           // Impresion en el log
           try{
               Object result = jp.getArgs();
               ObjectMapper objectMapper = new ObjectMapper();
               logger.debug("*============================ AFTER > > > {}", objectMapper.writeValueAsString(result));
           }catch (JsonProcessingException jpe){
                   logger.error("*JsonGenerationException > > > {}", jpe.toString());
           }catch (Throwable e){
                   logger.error("*============================ Throwable > > > {}", e.toString());
           }

     }

     /**
      * <p>Metodo: Control de Excepciones.</p>
      * <p>Descripcion: Este metodo se ejecuta cuando se lanza una excepcion.</p>
      * @param e de tipo Exception
      */
     //@AfterThrowing(pointcut="infoLoggerPointcut() && controllerPointcut() && requestMappingPointcut() && operacionesGetPointcut()", throwing="error")
     @AfterThrowing(pointcut="infoLoggerPointcut()", throwing="error")
     public void controlException(JoinPoint joinPoint, Throwable error){
            // Impresion en el log
            logger.debug("*============================ SE PRODUJO UNA EXCEPCION > > > {} CAUSA {}", joinPoint.getSignature().getName(), error);
     }

     /**
      * <p>Metodo: Impresion de Parametros de entrada.</p>
      * <p>Descripcion: Metodo que muestra los valores de los parametros de entrada de cada procedimiento.</p>
      * @param pjp del tipo ProceedingJoinPoint
      * @return the object
      * @throws Throwable the throwable
      */
    //@Around("infoLoggerPointcut() && controllerPointcut() && requestMappingPointcut() && operacionesGetPointcut()")
      @Around("infoLoggerPointcut()")
      public Object logMethod(ProceedingJoinPoint pjp) throws Throwable{
             // Definicion del objeto a retornar
             Object retval = null;
             try{
                // Definicion del String que imprimira los valores de los parametros
                StringBuffer stb = new StringBuffer();
                // Construccion del String
                stb.append("============================ INICIANDO METODO > > > " + pjp.getTarget().getClass().getName());
                stb.append("(");
                // Obtenemos los parametros de entrada de cada procedimiento
                Object[] arg = pjp.getArgs();

                // Recorremos los parametros para extraer sus valores
                for (int i = 0; i < arg.length; i++){
                     stb.append(arg[i]).append(",");
                }

                // Validamos la longitud de los parametros
                if (arg.length > 0) {
                    stb.deleteCharAt(stb.length() - 1);
                }
                // Cerramos el String para la impresion de los parametros
                stb.append(")");
                // Asignamos los objetos para retornar
                retval = pjp.proceed();
                // Imprimimos en el Log
                logger.debug("*============================ AROUND > > > {}",stb.toString());
             }catch(Throwable tr){
                    logger.error("============================ ERROR > > > {}", tr);
                    throw tr;
             }
             // Retornamos el valor
             return retval;
      }

      /**
       * <p>Metodo: Impresion en el log despues de retornar los valores.</p>
       * <p>Descripcion: Metodo que imprime en el log despues de retornar los valores.</p>
       * @param jp del tipo JoinPoint
       * @param result del tipo Object
       * @throws JsonProcessingException
       */
      //@AfterReturning(pointcut = "infoLoggerPointcut() && controllerPointcut() && requestMappingPointcut() && operacionesGetPointcut()", returning="result")
      @AfterReturning(pointcut = "infoLoggerPointcut()", returning="result")
      public void logAfterReturning(JoinPoint jp, Object result){
             // Impresion en el log
             try{
                 ObjectMapper objectMapper = new ObjectMapper();
                 logger.debug("*============================ DESPUES DEL RETURNING > > > {}", objectMapper.writeValueAsString(result));
             }catch (JsonProcessingException jpe){
                     logger.error("JsonGenerationException > > > {}", jpe.toString());
             }

      }

}

16.3.- Agregamos Nuestro Aspecto como @Bean al Contexto Spring

En nuestra clase ApplicationContextConfig agregamos lo siguiente y que spring haga su magia:


@Bean
public LogginAspect myAspect() {
       return new LogginAspect();
}

16.4.- Anotando Métodos

Ya lo único que nos queda es anotar cualquier método de nuestra aplicación que queramos ver sus parámetros en el log o su comportamiento o si se genera una excepción, para nuestro ejemplo anotaremos el método que obtiene todos los clientes:

aop1

 

16.5.- Aspectos en Tiempo de Ejecución

A continuación vemos la ejecución de nuestro aspectos:

aop2

aop3

17.- Principio HATEOAS

18.- Configuración Spring Framework

A partir de este punto comenzamos la configuración de la aplicación, definiremos los @Beans necesarios de Spring para que cargue el context, la conexión a la base de datos, y localice las configuraciones pertinentes:

18.1.- ApplicationContextConfig.java

package co.com.microservice.config.application;

import java.util.Properties;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
@ComponentScan({"co.com.microservice.application.*"})
@PropertySource(value = {"classpath:jdbc.properties"})
public class ApplicationContextConfig {

         @Autowired
         private Environment env;

         @Bean
         public DataSource dataSource() {
               DriverManagerDataSource dataSource = new DriverManagerDataSource();

               dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
               dataSource.setUrl(env.getProperty("jdbc.url"));
               dataSource.setUsername(env.getProperty("jdbc.user"));
               dataSource.setPassword(env.getProperty("jdbc.password"));

               return dataSource;
         }

         @Bean
         public LocalSessionFactoryBean sessionFactory(){
                   LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();

                   sessionFactory.setDataSource(dataSource());
                   sessionFactory.setPackagesToScan(new String [] {"co.com.microservice.application.model"});
                   sessionFactory.setHibernateProperties(getHibernateProperties());

                   return sessionFactory;
         }

         private Properties getHibernateProperties() {
                 Properties prop = new Properties();

                 prop.put("hibernate.dialect", env.getProperty("oracle.dialect"));
                 prop.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
                 prop.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
                 prop.put("hibernate.query.substitutions", env.getProperty("hibernate.query.substitutions"));
                 prop.put("hibernate.connection.release_mode", env.getProperty("hibernate.connection.release_mode"));
                 prop.put("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));

                 return prop;
        }

        @Bean
        @Autowired
        public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
               HibernateTransactionManager txtManager = new HibernateTransactionManager();
               txtManager.setSessionFactory(sessionFactory);

               return txtManager;
        }

}

18.2.- SecurityConfig.java

package co.com.microservice.config.security;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;

@Configuration
@EnableWebSecurity
@Import({OAuth2Configuration.class})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("customUserDetailsService")
    private UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
          auth.authenticationProvider(authProvider());
    }

    @Bean
    public DaoAuthenticationProvider authProvider() {
         DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
         authProvider.setPasswordEncoder(passwordEncoder());
         authProvider.setUserDetailsService(userDetailsService);

         return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
          return super.authenticationManagerBean();
    } 

    @Bean
    public PasswordEncoder passwordEncoder() {
        SecureRandom random = null;
        try {
             random = SecureRandom.getInstance("SHA1PRNG");
        } catch (NoSuchAlgorithmException e) {
                 e.printStackTrace();
        }
        PasswordEncoder encoder = new BCryptPasswordEncoder(16, random);
        return encoder;
   }

   @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
   private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {

       @SuppressWarnings("unused")
       public GlobalSecurityConfiguration() {

       }

       @Override
       protected MethodSecurityExpressionHandler createExpressionHandler() {
          return new OAuth2MethodSecurityExpressionHandler();
       }

   }

}

18.3.- OAuth2Configuration.java

En esta clase especifica definimos tanto nuestro servidor de autenticación como nuestro servidor de recursos, especificamos que recursos están securizados, si queremos persistir el token en memoria o base de datos, etc…

package co.com.microservice.config.security;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import co.com.microservice.application.component.CustomAuthenticationEntryPoint;
import co.com.microservice.application.utils.Constantes;

@Configuration
public class OAuth2Configuration {

         private static final String RESOURCE_ID = "microservice";

         @Configuration
         @EnableAuthorizationServer
         protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

                @Autowired
                @Qualifier("authenticationManagerBean")
                private AuthenticationManager authenticationManager;
 
                @Autowired
                private DataSource dataSource;
 
                @Autowired
                @Qualifier("customClientDetailsService")
                private ClientDetailsService clientDetailService;
 
                @Bean
                public JdbcTokenStore tokenStore() {
                       return new JdbcTokenStore(dataSource);
                }
 
                @Bean
                protected AuthorizationCodeServices authorizationCodeServices() {
                          return new JdbcAuthorizationCodeServices(dataSource);
                }
 
                @Override
                public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
                       oauthServer.allowFormAuthenticationForClients();
                }
 
                @Override
                public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                       endpoints
                             .tokenStore(tokenStore())
                             .authenticationManager(this.authenticationManager);
                }

                @Override
                public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                       // Con los datos del cliente en base de datos
                       clients.withClientDetails(clientDetailService);
 
                       // Con los datos del cliente en memoria
                       /*clients
                               .inMemory()
                               .withClient("clientapp")
                               .authorizedGrantTypes("password", "refresh_token", "client_credentials")
                               .authorities("USER")
                               .scopes("read", "write")
                               .resourceIds(RESOURCE_ID)
                               .secret("8649168")
                               .accessTokenValiditySeconds(300)
                               .refreshTokenValiditySeconds(600)
                               .autoApprove(false);*/ 
                }

                @Bean
                @Primary
                public DefaultTokenServices tokenServices() {
                        DefaultTokenServices tokenServices = new DefaultTokenServices();
                        tokenServices.setSupportRefreshToken(true);
                        tokenServices.setTokenStore(tokenStore());
                        return tokenServices;
                }

          }

          @Configuration
          @EnableResourceServer
          protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
 
                    @Override
                    public void configure(ResourceServerSecurityConfigurer resources) {
                           resources
                              .resourceId(RESOURCE_ID);
                    }

                    @Override
                    public void configure(HttpSecurity http) throws Exception {
                           http
                               .sessionManagement()
                               .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                               .and()
                               .csrf()
                               .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
                               .disable()
                               .authorizeRequests()
                               .antMatchers("/api/v1.0/secure").hasRole("ADMIN")
                               .antMatchers("/api/v1.0/**").authenticated()
                               .and()
                               .logout()
                               .logoutUrl("/oauth/logout");
                    }

          }

}

19.- Inicializando el contexto de Spring, Spring MVC y nuestra aplicación Web

19.1.- RestSecurityInit.java

package co.com.microservice.config.servlet;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class RestSecurityInit extends AbstractSecurityWebApplicationInitializer {

}

19.2.- WebInicializer.java

Este archivo es equivalente al web.xml

package co.com.microservice.config.servlet;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import co.com.microservice.config.application.ApplicationContextConfig;

public class WebInicializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
             return new Class[] { ApplicationContextConfig.class, RestViewConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
             return null;
    }

    @Override
    protected String[] getServletMappings() {
            return new String[] {"/"};
    }

}

19.3.- RestViewConfig.java

package co.com.microservice.config.servlet;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import co.com.microservice.config.security.SecurityConfig;

@EnableWebMvc
@Configuration
@ComponentScan({ "co.com.microservice.application.*" })
@Import({ SecurityConfig.class })
public class RestViewConfig {

        @Bean
        public InternalResourceViewResolver viewResolver() {
               InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
               viewResolver.setViewClass(JstlView.class);
               viewResolver.setPrefix("/WEB-INF/views/");
               viewResolver.setSuffix(".jsp");
               return viewResolver;
        }

}

20.- jdbc.properties

# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# * Parametros de Conexion con la Base de Datos Oracle *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.user=application
jdbc.password=application
jdbc.url=jdbc:oracle:thin:@localhost:1521:XE

# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# * Parametros de Configuracion de hibernate *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
oracle.dialect=org.hibernate.dialect.Oracle10gDialect
hibernate.show_sql=true
hibernate.query.substitutions=true 'T', false 'F'
hibernate.connection.release_mode=after_transaction
hibernate.generate_statistics=true
hibernate.format_sql=true
# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
# * Parametros del pool de conexiones para c3p0 *
# * * * * * * * * * * * * * * * * * * * * * * * * * * * *
c3p0.minPoolSize=5
c3p0.maxPoolSize=20
c3p0.timeout=300
c3p0.max_statement=50
c3p0.testConnectionOnCheckout=false

21.- Log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration debug="false">

          <appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
               <param name="File" value="${catalina.home}/logs/application_logger.log" />
               <param name="Append" value="true" />
               <param name="Threshold" value="ALL" />
               <param name="MaxFileSize" value="1MB" />
               <param name="MaxBackupIndex" value="1" />
               <param name="DatePattern" value="_yyyy-MM-dd" />
               <layout class="org.apache.log4j.PatternLayout">
                     <param name="ConversionPattern" value="%-5p %d [%t] %c: %m%n" />
              </layout>
          </appender>

          <appender name="console" class="org.apache.log4j.ConsoleAppender">
              <param name="Append" value="true" />
              <param name="Threshold" value="ALL" /> 
              <param name="DatePattern" value="_yyyy-MM-dd" />
              <layout class="org.apache.log4j.PatternLayout">
                    <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" />
              </layout>
         </appender>

        <root>
             <level value="DEBUG" />
             <appender-ref ref="file" />
             <appender-ref ref="console" />
        </root>
</log4j:configuration>

22.- index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
     pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
      <head>
            <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
            <title>RestFul Services - Buenas Practicas</title>
            <style type="text/css">
                           #table_7_1 {
                                       width: 900px;
                                       margin: 0 auto;
                           }
 
                           table {
                                  width: 900px;
                                  display: table;
                                  border-color: gray;
                                  border: 1px solid #CAD5CF;
                                  border-spacing: 0;
                                  border-collapse: collapse;
                                  cellspacing: 0;
                           }     
 
                           table.property th {
                                              font-size: 14px;
                                              text-align: left;
                                              vertical-align: text-center;
                                              width: 110px;
                                              border: 1px solid #CAD5CF;
                                              background: #F6F6F9;
                                              height: 50px;
                                              margin:10px;
                           }
 
                           table.property td {
                                              font-size: 14px;
                                              text-align: left;
                                              vertical-align: text-center;
                                              /*width: 110px;*/
                                              border: 1px solid #CAD5CF;
                           } 
 
                           table,table td,table th .table,.table td,.table th {
                                              line-height: 1.5;
                           }
 
                           th {
                                text-align: left;
                           }
 
                           td,th {
                                  display: table-cell;
                                  vertical-align: inherit;
                                  margin:5px;
                                  padding:5px;
                           }
 
                           th {
                               font-weight: bold;
                           }
 
                           body {
                                 font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                                 font-size: 14px;
                                 line-height: 1.42857143;
                                 color: #333;
                                 background-color: #fff;
                           }
               </style>
      </head>

      <body>
            <div class="table" id="table_7_1">
 
                    <p align="center">
                           <strong>RestFul Services - Buenas Practicas</strong>
                    </p>
 
                    <!-- Tabla de informacion -->
                    <table class="property" >
                       <thead>
                              <tr>
                                  <th scope="row">Proyecto</th>
                                  <td><strong class="title">Proyecto de Integraci&oacute;n de Tecnologias y Frameworks</strong></td>
                              </tr>
                       </thead>
                       <tbody>
 
                              <tr>
                                  <th scope="row">Autor</th>
                                  <td>Ing. Wilman Alberto Ortiz Navarro</td>
                              </tr>
                              <tr>
                                  <th scope="row">E-Mail</th>
                                  <td>-</td>
                              </tr>
                              <tr>
                                  <th scope="row">Fecha</th>
                                  <td>27-Sept-2016</td>
                              </tr>
                               <tr>
                                   <th scope="row">Descripci&oacute;n</th>
                                   <td>El siguiente proyecto tiene como finalidad mantener un historico de todos los frameworks que he ido aprendiendo en el camino a manejar toda su configuracion he implementacion</td>
                               </tr>
                   </tbody>
           </table>
 
           <p></p>
 
           <!-- Tabla de Frameworks -->
           <table class="property" >
                <tbody>
 
                      <tr>
                          <th scope="row">Java Version</th>
                          <td>
                              <ul>
                                  <li type="square">JDK 1.7.0_72</li>
                              </ul>
                          </td>
                      </tr>
                      <tr>
                          <th scope="row">Entorno de Desarrollo</th>
                          <td>
                              <ul>
                                  <li type="square">Version: Luna Service Release 2 (4.4.2)</li>
                                  <li type="square">Build id: 20150219-0600</li>
                                  <li type="square">Description: Eclipse Java EE IDE for Web Developers.</li>
                              </ul>
                          </td>
                      </tr> 
                      <tr>
                          <th scope="row">FrameWorks</th>
                          <td>
                              <ul>
                                  <li type="square">Maven 3.2.5</li>
                                  <li type="square">SpringFramework - 4.1.6.RELEASE</li>
                                  <li type="square">Spring Security - 4.0.1.RELEASE</li>
                                  <li type="square">Spring OAuth2 - 2.0.8.RELEASE</li>
                                  <li type="square">Hibernate - 4.3.6.Final</li>
                                  <li type="square">Log4j - 1.2.17</li>
                                  <li type="square">Slf4j - 1.7.6</li>
                                  <li type="square">Oracle JDBC - 11.2.0</li>
                                  <li type="square">Aspectj - 1.7.3</li>
                                  <li type="square">Jackson Core - 2.3.3</li>
                                  <li type="square">Servlet - 3.1.0</li>
                              </ul>
                          </td>
                      </tr>
                      <tr>
                          <th scope="row">Informaci&oacute;n</th>
                          <td>Para el desarrollo de este servicio se utilizaron las librerias descritas anteriormente, se aconseja utilizarlas para este proyecto ya que garantizan la compatibilidad y el funcionamiento con Apache Tomcat 8.0.15</td>
                      </tr>
                 </tbody>
          </table>
 
          <p></p>
 
          <center>
                  <a href="https://willmanortiz.wordpress.com/">Wilman Ortiz - WordPress</a>
           </center>
 
       </div>
 
    </body>
</html>

23.- web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.1"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
          <display-name>MicroServices</display-name>
          <welcome-file-list>
                   <welcome-file>/WEB-INF/index.jsp</welcome-file>
          </welcome-file-list>
</web-app>

24.- Ejecución

Ejecución Servidor
Ejecución Servidor

 

actualizacion_1actualizacion_2

actualizacion_3

En la siguiente imagen podemos observar el manejo de los códigos Http:

actualizacion_4

 

Usando curl:

– Obteniendo el Token:

curl -X POST -d     ‘grant_type=password&client_id=clientapp&client_secret=987654321&username=root&password=123456’ ‘http://localhost:8086/MicroServices/oauth/token&#8217; | json_pp

curl_token

– Consultando Productos:

curl -X GET -H ‘Content-Type: application/json’ -H ‘Authorization: Bearer c570b9e0-c7ef-4d1d-b246-b2b1dc703784’ ‘http://localhost:8086/MicroServices/api/v1.0/productos/paginacione=2&#8217; | json_pp

curl_token_2

– Consultando Clientes:

curl -X GET -H ‘Content-Type: application/json’ -H ‘Authorization: Bearer c570b9e0-c7ef-4d1d-b246-b2b1dc703784’ ‘http://localhost:8086/MicroServices/api/v1.0/clientes&#8217; | json_pp

curl_token_3

Ejecución en Vivo

25.- Código fuente

Desde GitHub pueden descargar el código fuente de la aplicación y hacer sus propias pruebas.

26.- Enlaces de interés

De antemano agradecer a todas aquellas personas que están dispuesta a compartir su conocimiento con los demás, acá les dejo los enlaces que me permitieron comprender la temática, aclarar dudas y poder lograr mi propósito de condensar en un solo proyecto todas estas tecnología.

PENDIENTES

  • Agregar aspectos
  • Construir BPELs de escritura y lectura de la cola y guardar en la base de datos
  • Construir el webservices proxy
  • Crear el recurso en el servicio Rest
Anuncios

33 comentarios sobre “Spring Security + OAuth2

  1. saludos Ricardo, si efectivamente esa clase no la utilizo, fue algo que ví en blog recuerdo que como tenían varias entidades con características similares trataban de agruparla en una sola y después extenderla, yo intenté aplicarlo a mi ejemplo, pero después mi modelo lo cambie y ya no fue necesario la subí para tenerla de recuedo.

    1. Saludos Javier,

      muchas gracias por avisarme no me había dado cuenta que el enlace estaba roto, aún sigo haciendo cambios en el código con cosas que estoy investigando, de todas formas volví a subir los fuentes actualizados por favor intenta nuevamente y si tienes problemas con el enlace me comentas.

      feliz día

  2. Willman ya pude descargar el proyecto , muchas gracias me va a servir bastante de ejemplo por que actualmente estoy realizando una implementación similar, quedo atento a nuevas publicaciones y lo felicito por la calidad de las mismas.

    feliz día.

    1. Saludos Javier,

      excelente que te sirva el aporte, sí la idea es continuar con la publicación y el próximo blog es crear un registro en base de datos y generar un clientId y clientSecret aleatorio con Spring MVC(es una tarea pendiente que tengo, porque siempre había trabajado con JSF y no lo he manejado) y después si desde una aplicación android(aplicando buenas practicas y los últimos frameworks) para consumir todo esto que he creado

    1. Saludos Gerardo,

      excelente que te sirva, en los próximos días voy a actualizar las versiones de los jar en el pom.xml y agregar algo sobre como aplicar el principio HATEOAS(Hipermedia As The Engine Of Application Status) con el módulo de spring y algo sobre una buena implementación sobre programación orientada a aspectos que encontré.

  3. buenas tardes willman, te escribo por que en la implementacion del proyecto me salio la siguiente traza de error pero no se por que se debe, por favor podrias indicarme cual puede ser la causa del error:

    15:47:46,493 ERROR [ClientCredentialsTokenEndpointFilter] An internal error occurred while trying to authenticate the user.
    org.springframework.security.authentication.InternalAuthenticationServiceException
    at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:125)
    at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:143)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:167)
    at org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter.attemptAuthentication(ClientCredentialsTokenEndpointFilter.java:123)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)

    De antemano muchas gracias

  4. te dejo otro pedacito q tal vez pueda servir:

    Caused by: java.lang.NullPointerException
    at co.com.microservice.application.service.security.CustomClientDetailService.loadClientByClientId(CustomClientDetailService.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

  5. hola willman ya encontre el problema , la causa era un null que habia en la tabla OAUTH_CLIENT_DETAILS en el campo autoapprove el codigo evaluaba un true pero no podia controlar si llegaba null

  6. ahora me salio esto:

    [ExceptionHandlerExceptionResolver] Invoking @ExceptionHandler method: public org.springframework.http.ResponseEntity org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.handleHttpRequestMethodNotSupportedException(org.springframework.web.HttpRequestMethodNotSupportedException) throws java.lang.Exception
    16:34:51,181 INFO [TokenEndpoint] Handling error: HttpRequestMethodNotSupportedException, Request method ‘GET’ not supported
    16:34:51,181 DEBUG [HttpEntityMethodProcessor] Written [error=”method_not_allowed”, error_description=”Request method ‘GET’ not supported”] as “application/json;charset=UTF-8” using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@2dab5a]
    16:34:51,181 DEBUG [DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name ‘dispatcher’: assuming HandlerAdapter completed request handling
    16:34:51,181 DEBUG [DispatcherServlet] Successfully completed request

  7. hola willman parece ser que esta haciendo falta el controller y los métodos de generación del token ya los busque pero no están y el error indica que no se encuentra el método oauth/token, sera que el proyecto q subiste era una versión anterior

    1. Saludos Javier,

      disculpa la demora en responder apenas salgo del trabajo, así está como dificil, podrías darme mas detalles, en que momento exacto te salta la exception? según es cuando tratas de ejecutar un método en uno de los @RestControllers si revisas el código del controller, que método estás ejecutando y mira este atributo en el @RequestMapping(method = RequestMethod.GET) que operación HTTP aplica para el método, desde la distancia parece que no estás usando la operación HTTP correcta, prueba y me comentas de todas formas voy a subir la última versión del proyecto.

      cualquier cosa quedo atento

      1. hola willman, te cuento el problema surge cuando quiere ejecutar el metodo /oauth/token es decir cuando se quiere generar el token, sale un error que dice : Did not find handler method for [/oauth/token] y revisando mas abajo Written [error=”method_not_allowed”, error_description=”Request method ‘GET’ not supported”] entonces entre a revisar los controller todos los los que hay pero ninguno tiene la notacion /oauth/token o una implementacion parecida par aun GET, entonces pienso que es que no encontro el metodo de generacion de token, lo demas lo esta haciendo bien se conecta a la bd no me esta generando errores solo me sale el mensaje q te digo q no encuentra un metodo en el servicio para generar el token.

    1. Saludos Javier,

      el recurso /oauth/token/ es un recurso del framework de spring el mismo lo expone, osea el recurso /oauth/token es de caja no lo vas a encontrar mi @RestController porque es algo que spring security ya hace por tí, por otra parte cuando vas a llamar ese recurso tienes que hacerlo con una operación POST del protocolo http usando el comando CURL de linux por ejemplo –> curl -X POST -H ‘Content-Type: application/json’ -H ‘Authorization: Bearer f04c049e-1fae-4d67-ab6f-af8dc3b74cd1’ ‘http://localhost:8086/MicroServices/api/v1.0/clientes’ | json_pp, la mayoria por no decir todos los clientes rest te permiten escoger la operación, pero ten presente que siempre que requieras el token tienes que usar la operación POST

    2. se me pasó comentarte que yá subí la última versión de código, lo he ido actualizando con otros topicos que me falta comentar, pero igual te funciona, saludos y cualquier duda no dudes en preguntar yo siempre estoy pendiente

  8. Willman muchas gracias el error era que estaba usando el metodo GET lo cambie a POST y ya esta funcionando, te queria hacer una preguntas para aclarar los conceptos.

    1. ese comando curl debo utilizarlo para poder hacer uso de los metodos del servicio cuando este escribiendo clases que lo consuman?

    2. por que cuando ejecute esta url que tienes de ejemplo para consultar clientes
    http://localhost:8082/MicroServices/api/v1.0/clientes/4567654999?access_token=9eb94afc-55fe-4a7b-b4f2-9d87e7e0cfcd
    sale el error 415 : 415 unsupported media type rest java

    3. para una solucion de una aplicacion que se va a separar por servicios la idea es tener un servicio que genere tokens de acceso y que los demas servicios utilicen el token q se genera cuando se loguean se podria realizar con esta implementacion oauth2 o tendria que recurrir a jwt , la duda es por que entiendo que oauth genera un token y este se envia a los ws que lo van a consumir pero en el caso de una aplicacion que esta partida en servicios donde guardaria el token para q sea utilizado en la invocacion de cada servicio, la aplicacion podria tener un servicio de consultas otro de generacion de archivo etc.. pero la idea es q un solo servicio les de acceso algo asi como este ws q genera token., si me puedes ilustrar un poco te lo agradeceria por que en este momento me encuentro pensando ese tema del diseno.la duda es por que el uso de los demas servicios no es secuencial despues de generar un token si no cuando el usuario acceda a alguna funcionalidad que requeira servicio web. yo habia pensado subir el token a la sesion o utilizar jwt que leyendo encontre q lo usan para generar token para aplicaciones de este tipo , la idea es q me gustaria hacer todo en este tipo de implementacion oauth2 pero no se q tan viable sea o si debo utilizar algo como jwt.

    muchas gracias por tu colaboración, se nota que soy nuevo en esto de los servicios rest.

    gracias

    1. Saludos Javier,

      1. no el comando curl míralo como otro mecanismo para probar tus servicios rest es como otro cliente, chrome por ejemplo tiene varios plugins que te permiten probar tus servicios como el Advance Rest Client o como el cliente que yo uso en mozilla el Rest Client.

      2.- el error 415 lo que te dice es que tienes que especificar el formato con el que se procesará el request/response en el caso del plugin de mozilla que yo utilizo el Rest Client me da la opción de especificar esto “Content-Type: application/json” en el header de mis request.

      3.- jwt es Json Web Token? hasta donde se es otro tipo de token yo hasta el momento no me he visto en la necesidad de utilizarlo igual spring tiene las opciones para usar este tipo de token. respecto a la forma en la que tienes que implementar tu solución, yo el año pasado estuve investigando lo mismo como separar mi servidor de autenticacion/autorización de mi servidor de recursos(Mi Servicio Rest) pero no encontré información o un ejemplo que me diera una luz, actualmente google, facebook tweter, github exponen servidores de autorización esto es, si tienes una cuenta con estos proveedores tu puedes registrar tu aplicación y ellos te dan tu clientId y tu clientSecret y ya con eso lo único que necesitas es que los usuarios finales de tu aplicación tienen que tener cuentas con ellos y cada vez que se autentiquen google, facebook, twiter , etc… te devolvuen el token y el resto es igual a lo que expuesto en este blog, ahora a principio de año encontré que hay una empresa creo que india que vende soluciones de ESB(Enterprise Service Bus) que tiene su propia implementación del OAut2 tu mismo te la puedes descargar la instalar y fucniona como tu servidor OAuth2, sinceramente no profundicé en ella pero quizas te sirva para lo que necesitas implementar y así te ahorras desarrollar tu servidor de autenticació/autorización, revisa este link http://wso2.com/library/articles/2014/02/securing-your-web-service-with-oauth2-using-wso2-identity-server-1/.

      1. tenias razon ya me funciono el ejemplo voy a investigar ese tema del servidor de seguridad que me comentas por que asi como lo habia pensado lo unico q me inquieta es q si cada servicio de mi aplicacion lo va a utilizar me toca hacer que se genere un token nuevo por cada uso de cada servicio y me tocaria subir a sesion los datos de login para poder generar el token y enviarselo a cada servicio cada vez que utilicen alguno, una ultima pregunta que arquitectura de capas utilizaste para separar los paquetes veo que me va tocar aprender a usar spring por q es bastante util.

        y lo ultimo si quiero implementar Gzip en las respuestas de los controller como lo podria hacer?

        muchas gracias por toda la ayuda que me has brindado he aclarado muchas dudas con tu apoyo.

      2. Saludos Javier,

        pues generalmente uno distribuye los paquetes para se se sobreentienda que almacenan cuando integras spring+hibernate, spring te da la posibilidad de aislar las capas investiga sobre estas anotaciones @Repository y @Service y en el momento que tu lo necesites integrarlas por decirlo de alguna manera con @Autowire.

        cuando mencionas GZIP es porque quieres comprimir el contenido del response creo, hasta el momento no me ha tocado hacerlo pero púedes ver este link http://www.oodlestechnologies.com/blogs/Gzip-Servlet-Filter-in-Spring-MVC

  9. Gracias por compartir conocimientos willmanortiz1027.Estoy probando tu proyecto pero me encuentro con que no están accesibles las clases:
    import com.integracion.web.services.types.LibroType;
    import com.integracion.web.services.types.RequestType;
    import com.integracion.web.services.types.ResponseType; requeridas en el controlador RestJMSProducerService.

    ¿Está actualizado el proyecto o estoy haciendo algo mal?.

    Gracias de nuevo.

    1. saludos Antonio,

      hace unos días cambie el *zip y subí el proyecto a github y el jar que hace la gestión con el JMS lo tengo instalado en mi repositorio local de Maven, te lo envio al correo el jar y lo agregas al classpath o lo instalas en tu maven con el siguiente comando: mvn install:install-file -Dfile='{ruta donde tengas el jar}\bookWebServicesProxy.jar’ -DgroupId=com.personal.book.bookservice -DartifactId=bookservice -Dversion=0.1 -Dpackaging=jar -DgeneratedPom=true.

      En esta entrada puedes ver como generarlo https://willmanortiz.wordpress.com/2016/02/10/web-services-proxy/

    1. Saludos Hugo,

      la parte del front no es mi fuerte, de hecho he comenzado a estudiar angular, un compañero implementó la parte del back con oauth y me dijo que las credenciales viajaban en texto plano y tuvo que cambiar de enfoque y utilizó spring thymeleaf con html5 y demas, exactamente por ese problema de seguridad quizas sea por ese camino.

      1. Gracias por tu respuesta,
        Lo único que se me ocurre es en mi frontend con sprint es crear webservices en el servidor del frontend y este consuma los servicios del servidor de recursos pasandole el token y angular consumiría la webservices del frontend, para angular no existiría token porque este se usaria en el lado servidor del frontend, si algún amigo tiene una mejor idea que puedas compartirla se agradecería =)

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s