Skip to main content

Blog

Imagen decorativa con bola del mundo y usuario

Recuperar el usuario que invoca un método de un servicio REST (JAX-RS)

En esta entrada veremos cómo recuperar información del usuario que invoca un método de nuestra API REST, siempre que implemente el estándar JAX-RS. Para ello haremos uso del contexto de seguridad (SecurityContext) que podemos inyectar tanto en nuestro método como en la clase.

El contexto de seguridad  almacena información relativa a la seguridad dentro del contexto de la petición. Entre los métodos que tenemos que implementar está “isUserInRole” que devuelve un booleano indicando si el usuario logado tiene el rol pasado como parámetro al método. También almacena el nombre del usuario.

Podemos utilizar la información del contexto de seguridad, por ejemplo, para realizar alguna operación especial o comprobación, en función de los roles del usuario que realiza la petición. También podemos recuperar el usuario que realiza una determinada operación para guardar esa información como parte de la auditoría de nuestra aplicación.

A continuación mostramos como inyectarlo y usarlo en un método:

@GET
@Produces({MediaType.APPLICATION_JSON+";charset=utf-8"})
@Secured
public Pelicula findPeliculas(@Context SecurityContext secContext, @QueryParam("titulo") String titulo, @QueryParam("nombreActor") String nombreActor) throws Exception{
		
		//Podemos comprobar si el usuario tiene un rol determinado
		if(secContext.isUserInRole("ADMINISTRADOR")) {
            //Realizar alguna operación específica para el administrador
        }

		//Podemos recuperar el nombre del usuario
		String nombre = null;
        Principal p = secContext.getUserPrincipal();
        if(p != null) {
            nombre = p.getName();
        }
		        
        // Habría que llamar a un servicio de búsqueda de películas
        return getPeliculaEjemplo(); 
    }

La anotación @Context es la que nos permite inyectar de un modo sencillo el contexto de seguridad.

Si queremos inyectarlo como atributo de la clase que contiene los métodos pondríamos:

@Path("/pelicula")
public class PeliculaResource {

    @Context SecurityContext secContext;

...

Espero que esta entrada te haya resultado útil. Por favor, valórala y deja tu comentario si tinenes cualquier duda.

imagen decorativa de candado virtual

Roles (autorización) en servicios REST con JAX-RS y token JWT

En esta entrada explicaremos como añadir una autorización basada en roles a un servicio REST Java hecho con JAX-RS y securizado con token JWT.

Las herramientas que debemos tener instaladas en nuestro equipo para el ejemplo son:

  • JDK 8
  • Un servidor Java. Para nuestro ejemplo utilizaremos Tomcat 8

Debemos tener también nuestra aplicación web Java creada con algunos servicios REST. Si no es así, puedes ver como crearla de forma sencilla en esta página: https://www.infointernet.es/internet-avanzado/java/crear-servicios-rest-json-una-aplicacion-web-java/.

En la siguiente entrada podemos ver cómo proteger los servicios REST mediante el uso de token JWT, con la librería JJWT: http://www.infointernet.es/internet-avanzado/java/securizar-servicios-rest-java-jax-rs-jwt/

Una vez que tenemos nuestros servicios REST protegidos con usuario y contraseña, es el momento de añadir autorización por roles. De este modo podremos dar permisos a ciertos usuarios para realizar determinadas operaciones, mientras se los denegamos a otros usuarios.

Usar la anotación @RolesAllowed

La anotación @RolesAllowed pertenece al estándar de Java (JSR 250). Se puede utilizar tanto en clases como en métodos concretos. Sirve para definir los roles que pueden acceder a dichas clases o métodos. Siguiendo con nuestro ejemplo, si tenemos un rol “ADMINISTRADOR” que es el único que puede ejecutar un método para añadir películas, pondríamos la anotación en dicho método del siguiente modo:

    @PUT
    @Secured
    @RolesAllowed({"ADMINISTRADOR"})
    public void addPelicula() throws Exception{
		
        // Habría que llamar a un servicio para crear películas
 
    }

La anotación @Secured es la que nos creamos a medida en el ejemplo de securización para asociar el método al filtro de seguridad.

Añadir la información del usuario y sus roles al token de seguridad (JWT)

En el cuerpo (playload) de un token JWT puede viajar cualquier propiedad (claim) que nosotros creemos. En nuestro caso, crearemos una llamada “roles” en la que pondremos los roles del usuario separados por coma. El nombre de usuario viajará en una de las propiedades por defecto que define el estándar y que se denomina “subject”. Con esta información será suficiente para saber en cada petición si un usuario tiene permisos para ejecutar un método o no, según las anotaciones @RolesAllowed que le afecten.

En nuestro ejemplo, tendremos que modificar el método utilizado para crear el token (JJWT). Ahí añadiremos una propiedad inicial con los roles de usuario logado. Dichos roles provendrán de una base de datos, etc. De este modo nuestro método de creación del token quedaría así:

private String issueToken(String login, String roles) {
    	//Calculamos la fecha de expiración del token
    	Date issueDate = new Date();
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(issueDate);
    	calendar.add(Calendar.MINUTE, 60);
        Date expireDate = calendar.getTime();
        
		//Creamos el token
        String jwtToken = Jwts.builder()
        		.claim("roles", roles)
                .setSubject(login)
                .setIssuer("http://www.infointernet.es")
                .setIssuedAt(issueDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, RestSecurityFilter.KEY)
                .compact();
        return jwtToken;
    }

Hemos introducido los roles en el propio token para no tener que estar haciendo peticiones a base de datos en cada petición para recuperar los roles del usuario que realiza dicha petición. Los datos que viajan en el token son seguros e inalterables, porque en caso de modificación, el token dejaría de ser válido. De ahí que podamos utilizarlo para almacenar información de autorización.

Crear un bean que represente al usuario y que implemente Principal

Crearemos un bean con las propiedades del usuario logado que deseemos tener accesibles en nuestros métodos REST. Dicha clase debe implementar la interfaz del estándar Java llamada “Principal“.

public class User implements Principal {
	
	private String username;
	private List roles;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public String getName() {
		return username;
	}

	public List getRoles() {
		return roles;
	}

	public void setRoles(List roles) {
		this.roles = roles;
	}
	

}

 

Crear una clase que implemente SecurityContext

La interfaz SecurityContext forma parte del estándar de Java y sirve para almacenar información relativa a la seguridad. Entre los métodos que tenemos que implementar está “isUserInRole” que devuelve un booleano indicando si el usuario logado tiene el rol pasado como parámetro al método. Este método es el que se invocará con la anotación @RolesAllowed y determinará los permisos en la aplicación.

Nuestra clase quedaría así:

public class MyApplicationSecurityContext implements SecurityContext {
	
	private User usuario;
	private boolean secure;
	
	public MyApplicationSecurityContext(User usuario, boolean secure){
		this.usuario = usuario;
		this.secure = secure;
	}

	@Override
	public String getAuthenticationScheme() {
		return SecurityContext.FORM_AUTH;
	}

	@Override
	public Principal getUserPrincipal() {
		return usuario;
	}

	@Override
	public boolean isSecure() {
		return secure;	
	}

	@Override
	public boolean isUserInRole(String rol) {
		if (usuario.getRoles() != null) {
            return usuario.getRoles().contains(rol);
        }
        return false;
	}

}

Podemos ver que nuestra clase de contexto de seguridad almacena el bean con el usuario logado que hemos creado anteriormente. El atributo “secure” es un booleano que indica si el acceso es por https.

Modificar el filtro de seguridad para crear el contexto de seguridad en cada petición

Ahora debemos modificar la clase RestSecurityFilter que creamos en el ejemplo de securización. Crearemos una instancia del contexto de seguridad (MyApplicationSecurityContext) y la añadiremos al contexto de las peticiones (ContainerRequestContext) para que esté accesible en nuestros métodos y pueda actuar correctamente la anotación @RolesAllowed.

@Provider
@Secured
@Priority(Priorities.AUTHENTICATION)
public class RestSecurityFilter implements ContainerRequestFilter {
	
	public static final Key KEY = MacProvider.generateKey();
 
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
 
        // Recupera la cabecera HTTP Authorization de la petición
        String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
 
        try {
        	// Extrae el token de la cabecera
            String token = authorizationHeader.substring("Bearer".length()).trim();
 
            // Valida el token utilizando la cadena secreta
            Jws claims = Jwts.parser().setSigningKey(KEY).parseClaimsJws(token);
            
            //Creamos el usuario a partir de la información del token
            User usuario = new User();
            usuario.setUsername(claims.getBody().getSubject());
            String roles = (String) claims.getBody().get("roles"); 
            usuario.setRoles(Arrays.asList(roles.split(",")));
            
            // Creamos el SecurityContext
            MyApplicationSecurityContext secContext = new MyApplicationSecurityContext(usuario, requestContext.getSecurityContext().isSecure());
            
            //Seteamos el contexto de seguridad
            requestContext.setSecurityContext(secContext);
            
        } catch (Exception e) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }
}

Como podemos ver en los comentarios, estamos utilizando la información que viaja en el token para crear el usuario logado y el contexto de seguridad con los roles.

Registrar RolesAllowedDynamicFeature (Jersey)

En nuestro ejemplo estamos utilizando Jersey como implementación de JAX-RS. Para que funcionen las anotaciones @RolesAllowed en los métodos, debemos registrar la funcionalidad RolesAllowedDynamicFeature .  Lo haremos en la clase de creación aplicación REST:

@ApplicationPath("rest")
public class Application extends ResourceConfig {
    public Application() {
        packages("es.infointernet.rest");
        register(JacksonFeature.class);
        register(RolesAllowedDynamicFeature.class);
    }
}

Puedes descargarte el código del ejemplo en: https://github.com/mpecero/jaxrs-jwt-example

Espero que esta entrada te haya resultado útil. Por favor, valórala y deja tu comentario si tinenes cualquier duda.

Cara de mujer con números representando Internet

Quien soy

Llevo más de 15 años en el mundo del desarrollo de aplicaciones. Principalmente he participados en proyectos para la Administración pública, desarrollando sobre todo con Java: Struts, JSF, Spring, Hibernate/JPA,… También he tenido la oportunidad de trabajar con Moodle, Alfresco, Liferay y Pentaho, entre otros productos. Desde el 2016 estoy trabajando también en proyectos con Anguar 2 e Ionic.

Empecé como casi todo el mundo: programador en prácticas. Así hasta llegar a Analista funcional / Arquitecto Java, aunque nunca he dejado de desarrollar.

Algunos os preguntaréis cómo después de 15 años sólo soy analista. La explicación es que llevo la mayor parte del tiempo trabajando en Mérida y aquí es muy muy difícil ascender. Ahora mismo trabajo en una factoría, en la que hay un gerente y, después, estamos un grupo de analistas/referentes. En parte lo prefiero, porque me ha permitido seguir en contacto con la tecnología, sobre todo en este momento en el que estamos asistiendo al desarrollo de aplicaciones Javascript y aplicaciones híbridas para móviles.

Por si queréis saber algo más de mí, aquí os dejo mi curriculum en Linkedin: https://es.linkedin.com/in/maximino-pecero-s%C3%A1nchez-61608858

 

Bola del mundo con números y edificios

Qué es Infointernet

Infointernet es un proyecto personal que nace con el objetivo de ayudar a las personas que nos movemos en el mundo de Internet, sobre todo, en lo relativo al desarrollo web y móvil.

Somos muchos los que trabajamos en el desarrollo de aplicaciones, sin embargo la experiencia me dice que la mayoría está acostumbrada a encontrarse una arquitectura ya montada y a desarrollar “sin conocimiento”, como diría un antiguo compañero mío. Un ejemplo claro de esto es el de un compañero que decía que él no había desarrollado servicios REST, cuando había hecho muchos utilizando las anotaciones de Spring. Realmente no sabía lo que era REST y para qué servían realmente las anotaciones que estaba utilizando en las clases de servicio que desarrolló. Copiaba, pegaba y modificaba “sin conocimiento”.

Por último, vivimos en un mundo en constante cambio. Cuando empecé hace más de 15 años se utilizaba JSP con acceso a datos mediante JDBC directamente en las páginas y muy pocas clases. Después empezamos a trabajar con las aplicaciones desarrolladas en varias capas (presentación, servicios y datos) junto con distintos framworks: Struts e Hibernate primero, también el maravilloso Spring, luego apareció JSF y JPA… Y en estos últimos años se han puesto de moda las aplicaciones Javascript: Angular, React,… La tecnología evoluciona constantemente y los desarrolladores debemos adaptarnos para no quedarnos obsoletos en un mundo que no para. Esto hace que surjan nuevos retos a diario y que debamos acudir a nuestros buscadores favoritos para buscar ayuda constante.

Este blog pretendar aportar un pequeño granito de arena de información para desarrolladores de aplicaciones, así como aclaraciones puntuales a cerca de la resolución de ciertos problemas concretos con los que nos encontramos en nuestro día a día.

 

Securizar servicios REST Java: JAX-RS con JWT

En esta entrada mostraremos como utilizar tokens para securizar nuestros servicios REST hechos con JAX-RS. Los token los crearemos con la librería JJWT, que implementa el estándar JWT, que es el habitual para la creación de tokens.

Podríamos haber securizado los servicios mediante sesiones, como cualquier otra aplicación Java, sin embargo, esto no es lo habitual ni lo recomendado para servicios REST sin estado. Podéis ver más información al respecto en: https://ponyfoo.com/articles/json-web-tokens-vs-session-cookies

Las herramientas que debemos tener instaladas en nuestro equipo para el ejemplo son:

  • JDK 8
  • Un servidor Java. Para nuestro ejemplo utilizaremos Tomcat 8

Debemos tener también nuestra aplicación web Java creada con algunos servicios REST. Si no es así, puedes ver como crearla de forma sencilla en esta página: https://www.infointernet.es/internet-avanzado/java/crear-servicios-rest-json-una-aplicacion-web-java/.

Qué es JWT

JSON Web Token (JWT) es un estándar (RFC-7519 ) para crear un token de acceso.

Estos tokens están formados por tres partes:

  • Cabecera (header): es una cadena JSON cifrada en Base64 que contiene el tipo de token y la codificación utilizada para la firma
     Header = '{"alg": "HS256", "typ": "JWT"}'
  • Cuerpo (payload): Es un conjunto de información en formato JSON que contiene los “claims”, que no son más que un conjunto de campos predefinidos, así como algunos propios que podemos añadir, como los roles.
    Payload = '{“sub”:”j.rodriguez”,"iat": "1424180385", "exp": "1424190385",”roles”:”a,b,c”}'
  • Firma (signature): Está formado por la cabecera y cuerpo, encriptado con una clave secreta. Esta parte es la que garantiza la integridad de los datos, es decir, que los datos anteriores no han sido alterados:
     clave = 'secretkey'
     unsignedToken = encodeBase64Url (encabezado) + '.' + EncodeBase64Url (carga útil)
     signature = HMAC-SHA256 (clave, unsignedToken)
    

Estas partes se unen por un “.”. De este modo, el Token resultante podría tener un aspecto como el siguiente:

token = eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MDA5OTY4MDIsImV4cCI6MTUwMTAwMDQwMn0.GI-e8ala__SygMKviTShtLK9gXMTOKQPn0ktUh31988XyyGMBo4MbWLa1t71ybNuYplKhSjAaasxKbHWiMNwTA

Esta información viajará normalmente en la cabecera “Authorization” en los accesos a recursos protegidos.

Authorization: Bearer token

Puedes encontrar más información en https://carlosazaustre.es/blog/que-es-la-autenticacion-con-token/

Paso 1: Añadir dependencias

Debemos añadir a nuestro proyecto la dependencia de JJWT, que será la que nos permitirá generar y validar el token en formato JWT.

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.7.0</version>
</dependency>

Paso 2: Crear el filtro

El filtro será el encargado de interceptar las peticiones a recursos REST protegidos y validar el token recibido en la cabecera. Para ellos debe implementar la interfaz ContainerRequestFilter, que forma parte del estándar JAX-RS.

@Provider
@Secured
@Priority(Priorities.AUTHENTICATION)
public class RestSecurityFilter implements ContainerRequestFilter {
	
    public static final Key KEY = MacProvider.generateKey();
 
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
 
        // Recupera la cabecera HTTP Authorization de la petición
        String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
 
        try {
        	// Extrae el token de la cabecera
            String token = authorizationHeader.substring("Bearer".length()).trim();
 
            // Valida el token utilizando la cadena secreta
            Jwts.parser().setSigningKey(KEY).parseClaimsJws(token);
 
        } catch (Exception e) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }
}

Para generar la cadena secreta hemos utilizado la clase “MacProvider” de la propia libreria JWT. El método usado genera una clave secreta aleatoria de 512 bits. Podría utilizarse también alguna clave almacenada en un keystore.

La anotación “@Provider” será la que nos permitirá registrar automáticamente nuestro filtro en nuestra aplicación REST.

Paso 3: Anotación para vincular el filtro las clases y métodos protegidos

El filtro creado anteriormente no debe ejecutarse siempre, sino sólo en aquellos casos en los que el recurso esté protegido. Para ello crearemos una anotación que utilizará la meta-anotación “@NameBinding”, que forma parte de JAX-RS.

@javax.ws.rs.NameBinding
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface Secured {

}

Paso 4: Securizar los recursos con la nueva anotación

Los métodos y clases que queramos securizar deberán ir precedidos por la anotación “@Secured” que hemos creado en el paso anterior.

@Path("/pelicula")
public class PeliculaResource {
	
    @GET
    @Path("{idPelicula}")
    @Secured
    @Produces({MediaType.APPLICATION_JSON+";charset=utf-8"}) 
    public Pelicula getPelicula(@PathParam("idPelicula") Integer idPelicula) throws Exception{
		
        //Habría que llamar a un servicio que devolviera la película con el id pasado como parámetro
        return getPeliculaEjemplo(); 
    }
    ...

Cuando se invoque al método del ejemplo anterior, se ejecutará previamente el filtro, comprobando que viaja la cabecera “Authorization” con un token válido. En caso de que haya cualquier problema, se devolvería un error HTTP 401. Si todos es correcto, se daría paso a la ejecución del método del ejemplo.

Paso 5: Crear un método para la obtención del token

Creamos un nuevo recurso con un método público que nos permitirá realizar la autenticación y obtener un token en caso positivo.

@Path("/usuario")
public class UsuarioResource {
 
    @GET
    @Path("/login")
    public Response authenticateUser(@QueryParam("login") String login,
    		@QueryParam("password") String password) {
        try {
 
            // Aquí iría el código de validación del usuario y contraseñas proporcionados,
        	// por ejemplo validándolo contra una base de datos...
            //authenticate(login, password);
        	
            // Si todo es correcto, generamos el token
            String token = issueToken(login);
 
            // Devolvemos el token en la cabecera "Authorization". 
            // Se podría devolver también en la respuesta directamente.
            return Response.ok().header(HttpHeaders.AUTHORIZATION, "Bearer " + token).build();
 
        } catch (Exception e) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }
    }
 
    private String issueToken(String login) {
    	//Calculamos la fecha de expiración del token
    	Date issueDate = new Date();
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(issueDate);
    	calendar.add(Calendar.MINUTE, 60);
        Date expireDate = calendar.getTime();
        
        //Creamos el token
        String jwtToken = Jwts.builder()
                .setSubject(login)
                .setIssuer("http://www.infointernet.es")
                .setIssuedAt(issueDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, RestSecurityFilter.KEY)
                .compact();
        return jwtToken;
    }
}

Hemos utilizado para la creación del token la misma clave secreta que en la validación, para que todo funcione correctamente.

Puedes descargarte el código del ejemplo en: https://github.com/mpecero/jaxrs-jwt-example

Espero que esta entrada te haya resultado útil. Por favor, valórala y deja tu comentario si tinenes cualquier duda.

CORS

Peticiones Cross-domain con Javascript en servicios REST con JAX-RS (CORS)

En este artículo explicaremos como habilitar CORS en servicios REST hechos con JAX-RS, para resolver el problema de acceso a dominios cruzados con Javascript.

El problema

Si estamos desarrollando aplicaciones web modernas con Javascript (Ionic, Angular, etc.) es posible que nos hayamos encontrado con el siguiente error a la hora de realizar nuestras llamadas AJAX (XMLHttpRequest) a los servicios REST:

XMLHttpRequest cannot load http://miservicio.es/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mi.app' is therefore not allowed access.

Es un mecanismo de seguridad de los navegadores modernos, que impiden que se puedan realizar peticiones AJAX en una aplicación que se está ejecutando en un dominio y puerto a un servicio que está ejecutándose en otro dominio y/o puerto diferente. En concreto estamos violando la política del mismo origen (SOP).

La solución: CORS

Cross-origin resource sharing (CORS) es un mecanismo estándar definido por el W3C que permite que se hagan llamadas JavaScript con XMLHttpRequests a otro dominio diferente al que originó el código JavaScript. Tales solicitudes de “dominio cruzado” estarían prohibidas por los navegadores web, según la política de seguridad SOP.

Para habilitarlo tendremos que incluir una nueva cabecera HTTP en la respuesta: Access-Control-Allow-Origin . Si recuerdas el mensaje de error anterior, esto es exactamente lo que el navegador está tratando de decirte. Cuando un navegador recibe una respuesta de una fuente de origen cruzado, comprobará si hay cabeceras CORS. Si el origen especificado en el encabezado de respuesta coincide con el origen actual, permite el acceso de lectura a la respuesta. De lo contrario, obtendrá el mensaje de error.

Es más práctico que sólo permitir peticiones de mismo origen y es más seguro que simplemente permitir todas las solicitudes de origen cruzado.

Implementar CORS con Jersey (JAX-RS)

Si tenemos nuestros servicios REST implentados con Jersey, como explicamos en un articulo anterior, podemos aplicar CORS mediante un filtro. Este filtro modificará las peticiones y respuestas inclyendo las cabeceras definidas por CORS.

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class ResponseCorsFilter implements ContainerResponseFilter {

	@Override
	public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
		response.getHeaders().add("Access-Control-Allow-Origin", "*");
		response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, Authorization");
		response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
	}
}

En el ejemplo anterior, el filtro ResponseCorsFilter siempre agrega las siguientes cabeceras:

  • Access-Control-Allow-Origin: El “*” significa que la solicitud puede provenir de cualquier dominio. De esta manera hacemos que la API REST sea pública.
  • Access-Control-Allow-Methods: indica que se permiten los métodos GET, POST, DELETE, PUT para acceder al recurso.
  • Access-Control-Allow-Headers: indica que los encabezados Origin, Content-Type, Acept y Authorization se pueden utilizar al realizar la solicitud.

Los filtros en los servicios JAX-RS deben implementar la interfaz ContainerResponseFilter. Para registrar el filtro en nuestra aplicación ulizamos la anotación @Provider.

Espero que te haya sido útil el artículo. Si tienes cualquier duda o sugerencia, no dudes en escribirme.

Crear servicios REST con JSON en una aplicación web Java

Vamos a explicar cómo crear servicios REST que trabajen con JSON en una aplicación web Java.

Los servicios REST que devuelven JSON son cada vez más utilizados, porque se adaptan muy bien a las necesidades de los nuevos frameworks JavaScript, tanto para aplicaciones web (Angular, React,…) como para aplicaciones móviles híbridas (Ionic).

Las herramientas que debemos tener instaladas en nuestro equipo para el ejemplo son:

  • JDK 8
  • Un servidor Java. Para nuestro ejemplo utilizaremos Tomcat 8

Debemos tener también nuestra aplicación web Java creada. Si no es así, puedes ver como crearla de forma sencilla en esta página: http://www.infointernet.es/internet-avanzado/java/crear-una-aplicacion-web-java-eclipse-maven/.

Existen dos alternativas principales a la hora de crear los servicios REST en Java:

  • Con Spring MVC: útil si ya utilizamos este framework en nuestro proyecto o estamos muy habituados a él.
  • Con JAX-RS: es la especificación de Java para la creación de servicios REST. Esta será la opción que utilicemos en esta entrada, al ser la más simple y estándar.

Como hemos comentado anteriormente JAX-RS es una especificación, por lo que debemos utilizar alguna libería que lo implemente: RESTEasy (integrado en WildFly), Jersey (integrado en Glassfish), etc.

En nuestro ejemplo optaremos por Jersey, que es la de uso más extendido. Además, le añadiremos un módulo para trabajar con JSON llamado Jackson.

Paso 1: Añadir dependencias

Una vez tenemos nuestro proyecto Java gestionado con Maven, le añadiremos las dos siguientes dependencias:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.25.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.25.1</version>
</dependency>

Paso 2: Crear los beans

En nuestro ejemplo crearemos dos beans, uno para las películas y otro para los actores:

   public class Actor {
	
	private String nombre;

	public String getNombre() {
		return nombre;
	}

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

   }

   public class Pelicula {
	
	private String titulo;
	
	private List actoresList;

	public String getTitulo() {
		return titulo;
	}

	public void setTitulo(String titulo) {
		this.titulo = titulo;
	}

	public List getActoresList() {
		return actoresList;
	}

	public void setActoresList(List actoresList) {
		this.actoresList = actoresList;
	}

   }

Paso 3: Crear la aplicación JAX-RS y desplegarla en el servidor

JAX-RS proporciona una clase abstracta agnóstica de despliegue llamada Application que nos permitirá definir el paquete raíz donde están los servicios (recursos) REST así como como los provider. En nuestro caso, crearemos una clase que extiende ResourceConfig, que a su vez extiende a la clase Application.

@ApplicationPath("rest")
public class Application extends ResourceConfig {
    public Application() {
        // Paquete donde están nuestros servicios REST
        packages("es.infointernet.rest"); 
        // Registramos el Provider de JSON de Jackson
        register(JacksonFeature.class);
    }
}

Con la anotación @ApplicationPath estamos registrando nuestra aplicación en el servidor, además de indicar que estará escuchando en el path “rest”. También podríamos registrarla como un servlet, pero no vamos a complicar el ejemplo.

Paso 4: Crear los servicios

Ahora ya solo nos quedaría crear las clases para los recursos, donde indicaremos las URL y parámetros que recibirán. Veamos un ejemplo:

@Path("/pelicula")
public class PeliculaResource {
	
    @GET
    @Path("{idPelicula}")
    @Produces({MediaType.APPLICATION_JSON+";charset=utf-8"}) 
    public Pelicula getPelicula(@PathParam("idPelicula") Integer idPelicula) throws Exception{
		
        //Habría que llamar a un servicio que devolviera la película con el id pasado como parámetro
        return getPeliculaEjemplo(); 
    }
	
    @GET
    @Produces({MediaType.APPLICATION_JSON+";charset=utf-8"}) 
    public Pelicula findPeliculas(@QueryParam("titulo") String titulo, @QueryParam("nombreActor") String nombreActor) throws Exception{
		
        // Habría que llamar a un servicio de búsqueda de películas
        return getPeliculaEjemplo(); 
    }
	
	
    /**
    * Servicio de ejemplo. 
    * @return
    */
    private Pelicula getPeliculaEjemplo(){
	List actores = new ArrayList();
        
        Actor a = new Actor();
        a.setNombre("Penélope Cruz");
        actores.add(a);
        
        Pelicula p = new Pelicula();
        p.setTitulo("Jamón Jamón");
        p.setActoresList(actores);
       
        return p;
    }
}

Vamos a explicar un poco las anotaciones:

  • @Path: Nos sirve para especificar la URL donde van a estar publicados nuestros servicios. Se puede aplicar a una clase y/o a un método. Puede recibir valores entre llaves que corresponderían a parámetros ( @Path(“{idPelicula}”))
  • @GET: Indica que a ese método hay que invocarlo por el método HTTP GET. Se suelen utilizar:
    • GET: Para recuperar recursos
    • POST: Para añadir recursos
    • PUT: Para modificar recursos
    • DELETE: Para borrar recursos
  • @Produces({MediaType.APPLICATION_JSON+”;charset=utf-8″}): Con esto indicamos que el método devuelve JSON codificado en UTF-8. Es importante la codificación, para que los caracteres acentuados en español se devuelvan correctamente.
  • @PathParam: Se utiliza para mapear los parámetros pasados por el path de la URL
  • @QueryParam: Se utiliza para mapear los parámetros pasados por la URL

Para invocar a los métodos anteriores deberíamos poner, por ejemplo:

  • Primer método: http://localhost:8080/<nombe_aplicacion>/rest/pelicula/125
  • Segundo método: http://localhost:8080/<nombe_aplicacion>/rest/pelicula?titulo=jamón

Existen muchas más anotaciones que ofrecen muchas más posibilidades, pero se salen del objetivo del ejemplo. Si quieres profundizar más, puedes ver algunos ejemplos aquí: https://jersey.java.net/documentation/latest/jaxrs-resources.html

Espero que te haya sido de utilidad este artículo. Si tienes cualquier duda o comentario no dudes en escribirme.

Puedes descargar el código del ejemplo aquí: DemoRest. También puedes descargar el ejemplo completo, con seguridad incluida, de: https://github.com/mpecero/jaxrs-jwt-example

En próximos artículos explicaré como securizar estos servicios.

Crear aplicación web Java con Eclipse

Crear una aplicación web java con Eclipse y Maven

Vamos a explicar cómo crear una aplicación web Java con Eclipse y Maven.

Las herramientas que debemos tener instaladas en nuestro equipo para el ejemplo son:

En primer lugar seleccionamos la opción de menú “File -> New -> Maven Project”.

menú File -> New -> Maven Project

Nos aparecerá un cuadro de diálogo donde debemos seleccionar la opción “Create a simple project (skip archetype selection)”. Existen muchos arquetipos para aplicaciones web, pero si queremos un proyecto limpio, lo mejor es crearlo sin arquetipo.

Cuadro de diálogo New Maven Project

Al pulsar “Next”, nos solicitará el id de grupo y el id del artefacto. Le ponemos el que deseemos. Además debemos seleccionar la opción de empaquetado “war”.

Para terminar pulsaremos “Finish” y automáticamente tendremos creado el proyecto.

Aquí no termina el trabajo, porque el proyecto que genera no tiene el fichero descriptor de despliegue (web.xml) y la versión de Java con la que aparece es la 5. Podemos corregir esto haciendo clic con el botón derecho sobre el proyecto y eligiendo en el menú contextual la opción “Properties”. Nos aparecerá un cuadro de diálogo donde elegiremos la opción de menú “Project Facets” y cambiaremos la versión de Java a la 1.8.

Project Facets con Java 8

En esa pantalla también desmarcaremos la opción “Dynamic Web Module” y pulsaremos “Apply”. Posteriormente volveremos a marcar la opción “Dynamic Web Module” y veremos como aparece un enlace abajo llamado “Futher configuration available”. La pulsaremos e introduciremos la ruta del directorio de contenidos (“src/main/webapp”) y marcaremos la opción para que cree el descriptor de despliegue. Seleccionaremos la versión 3.1 del “Dynamic Web Module” y pulsaremos “Ok” para terminar.

Configuración de Dynamic Web Project

Con esto hemos conseguido que Eclipse nos cree el descriptor de despliegue para la especificación de Servlet 3.1. Deberíamos asegurarnos de que nuestro servidor soporta dicha versión. Para el ejemplo utilizamos Tomcat 8 que si lo soporta (http://tomcat.apache.org/whichversion.html).

Podemos crear nuestra primera página haciendo clic con el botón derecho sobre la carpeta “src/main/webapp” y seleccionado “new -> JSP File”. La llamaremos “index.jsp” y será nuestra página de inicio.

Menú New -> JSP File

Ahora ya solo tenemos que añadir nuestra aplicación al servidor Java que tenemos en Eclipse y arrancarlo. Podemos acceder a la aplicación en cualquier navegador poniendo la dirección: http://localhost:8080/<nombre_proyecto>

Aquí os podéis descargar el código del proyecto que hemos creado: DemoWeb.zip

Podéis importarlo en vuestro Eclipse mediante la opción “File -> Import” y seleccionando en el cuadro de diálogo “Existing Maven Project”.

Espero que os haya sido de utilidad esta entrada. Si tienes cualquier duda o sugerencia, deja tu comentario.

Cables de red

Recuperar la IP del cliente en Java tras un balanceador

Podemos recupera la IP del cliente que accede a nuestra aplicación web Java mediante el código:

String ipAddress = request.getRemoteAddr();

Ahora bien, si el servidor está detrás de un balanceador de carga lo que obtendremos siempre con la sentencia anterior es la IP del balanceador. En estos casos debemos obtener la IP que viaja en alguna de las cabecera de la petición HTTP, mediante el código:

     public String getIpAddr(HttpServletRequest request) {
         final String[] HEADER_LIST = { 
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR" 
        };

        for (String header : HEADERS_LIST) {
            String ip = request.getHeader(header);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip) && !isPrivateAdress(ip)) {
                return ip;
            }
        }
        return request.getRemoteAddr(); 
    }

    public boolean isPrivateAdress(String host) {
        try {
            return java.net.InetAddress.getByName(host).isSiteLocalAddress();
        } catch (UnknownHostException e) {
            LOGGER.warn("UnknownHostException en isPrivateAdress(String host)", e);
            return true;
        }
    }

El método “getIpAddr” devolvería la IP pública del cliente. Si lo que queremos es simplemente recuperar la IP pública o privada del cliente, aunque esté detrás de un proxy, tendríamos que quitar la comprobación de si la IP es privada:

     public String getIpAddr(HttpServletRequest request) {
         final String[] HEADER_LIST = { 
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR" 
        };

        for (String header : HEADERS_LIST) {
            String ip = request.getHeader(header);
            LOGGER.debug("Contenido del header "+ header + " / "+ ip);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                return ip;
            }
        }
        return request.getRemoteAddr(); 
    }

Espero que te haya resultado útil. Si tienes cualquier duda o problema con el código, déjame tu comentario.

Pinturas de colores

Cambiar el color de fondo o del menú de un tema de WordPress

Es posible cambiar el color de cualquier elemento de tu tema de WordPress (fondo, menú, texto,…) de  modo bastante sencillo, aunque el propio tema no te lo permita en sus opciones. Todo ello sin apenas conocimientos de CSS ni de HTML.

El primer paso es instalar un plugin que te permita modificar el CSS de tu tema, como por ejemplo WP Add Custom CSS, del que ya hablamos en otra entrada de este blog. El plugin añade una nueva opción de menú, llamada “Añadir CSS personalizado”, donde poder introducir el código CSS que sobreescribirá al del tema.

Menú Añadir CSS personalizado

Ahora debemos averiguar el estilo que debemos sobreescribir, para lo cual utilizaremos el inspector de código del navegador. Podemos abrirlo haciendo clic con el botón derecho sobre el elemento cuyo color queremos modificar y eligiendo la opción “Inspeccionar”

Menú contextual de Chrome con la opción Inspeccionar

Una vez pulsado nos aparecerá en la parte inferior de la pantalla el código HTML y el CSS del elemento. No te asustes, verás que sencillos es cambiarlo.

Inspeccionar código con el código seleccionado y los CSS

Hemos resaltado en la imagen el título, que es el que vamos a cambiar de color, y el estilo correspondiente. Podemos ver también que el título es el que está seleccionado en la parte del código.

Podemos probar diferentes colores directamente en la pantalla de nuestro navegador, haciendo clic sobre el color y eligiendo uno nuevo de la paleta o introduciendo su código hexadecimal.

Paleta de colores del navegador

Lo cambiaremos hasta que quede como deseamos y, después, seleccionaremos el código CSS donde está el color.

Código CSS seleccionado con el color deseado

Ahora simplemente debemos acceder a la opción de menú “Añadir CSS personalizada” y pegar nuestro código CSS en el campo “Reglas CSS”.

CSS escrito en el campo Reglas CSS

Pulsamos el botón “Guardar” y ya tenemos cambiado el color de todos los títulos de las entradas de nuestro tema. Podríamos haber hilado más fino borrando los otros estilos que no aplicaban en nuestro ejemplo (font-size y magirn-bottom).

Esto es aplicable a todos los elementos de la pantalla, aunque debemos tener precaución e ir navegando por nuestra web tras los cambios para ver que todo se sigue viendo como esperábamos.

Si tienes cualquier duda o comentario, escríbeme.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

CERRAR