Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Ventajas
1. JWT adems ofrece soluciones a la escalabilidad de la aplicacin al evitar
almacenar datos de sesin en el servidor, que generalmente sobrecargan la
memoria y el disco.
2. La informacin puede ser accedida y validad desde cualquier aplicacin.
3. CSRF Cross-site request forgery (CSRF) es un tipo vulnerabilidad que
afecta a aplicaciones web y que se basa en explotar la confianza que los
sitios web tienen con sus usuarios. Con autenticacin tradicional, a no ser
que utilicemos tcnicas para prevenirlo, estamos expuestos a este tipo de
ataques. Con la autenticacin basada en tokens, evitamos este tipo de
problemas.
Implementacin JWT en Spring MVC.
Lo primero que se debe hacer es interceptar todas las peticiones que lleguen al
servidor por medio de un filtro. La implementacin de este filtro se encuentra en el
siguiente repositorio: https://github.com/victacora/FilterSpringMVC.
En el archivo de configuracin web.xml copiar las siguientes lneas:
<filter>
<filter-name>
org.seratic.enterprise.tgestiona.web.filter.SecurityFilter
</filter-name>
<filter-class> filter.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Estas lneas corresponden a la configuracin del filtro, aqu se define que clase lo
implementa, y cules son las URL que deben ser interceptadas por l, en este
caso, todas. Cabe aclarar que no es posible excluir una URL en especfica
haciendo uso de este archivo de configuracin, mas adelant expondr en qu
forma se puede solucionar esta necesidad.
Al descargar el paquete del repositorio, se puede ver los siguientes archivos:
El valor de este key es aleatorio y puede estar conformado por cualquier valor. Si
desea
generar
una
clave
puede
utilizarse
este
enlace:
https://www.grc.com/passwords.htm
Las siguientes lneas corresponde a la generacin del token una vez el usuario ha
sido debidamente autenticado, este token es enviado al cliente y este debe
almacenarlo para poder ejecutar cualquier peticin posteriormente al servidor. El
token generado en este caso fue firmado haciendo uso del algoritmo HS256, se
estableci como subject el id del usuario, la fecha de entregado con la fecha
actual, y fecha de vencimiento del token un dia despus de haber sido generado.
El valor de Constantes.TIEMPO_EXPIRACION_TOKEN, corresponde a 20
minutos expresado en milisegundos 1200000. Este valor se recomienda que este
entre 20 minutos y 1 hora y que antes de expirar se renueve el token y as aadir
un mayor nivel de seguridad, tambin se puede ir almacenado para crear una lista
negra y evitar que se reutilice.
String token =
Jwts.builder().setSubject(String.valueOf(retorno.getCodigo())).signWith(Signatur
eAlgorithm.HS256, Constantes.KEY).setIssuedAt(new Date()).setExpiration(new
Date(Calendar.getInstance().getTimeInMillis() +
Constantes.TIEMPO_EXPIRACION_TOKEN)).compact();
5
}
catch(e) {
//la cadena no contiene un formato valido json
console.log('Error inesperado: '+e.message);
}
};
verificarRespuestaServicio=function(conn, req, opts)
{
try{
var json = Ext.JSON.decode(req.responseText);
if (typeof json
!=='undefined'&&json.metaData!==undefined&&json.metaData===666){
showErrorAuthentication(json.message);
}
}
catch(e) {
//la cadena no contiene un formato valido json
console.log('Error inesperado: '+e.message);
}
return true;
};
function reiniciar()
{
window.onbeforeunload = null;
var store = Ext.data.StoreManager.lookup("Store_Login");
if(typeof store !=='undefined'&&store.getCount()>0){
store.removeAll();
}
Ext.Ajax.request({
url: 'usuario/cerrarSesion.action',
method :'POST',
failure: function(response,action){
try{
var json = Ext.JSON.decode(response.responseText);
}catch(e){
Ext.Msg.alert('Informacin','Ha ocurrido un error, detalles:
'+json.message);
}
}
});
Ext.Ajax.un('beforerequest',agregarToken);
Ext.Ajax.un('requestcomplete',verificarRespuestaServicio);
window.location.href = window.location.href;
}
showErrorAuthentication = function(msj)
{
Ext.Msg.show({
title: 'Error',
msg: msj,
buttons: Ext.MessageBox.OK,
6
buttonText:{
ok: "Aceptar"
},
icon: Ext.MessageBox.INFO
});
setTimeout(reiniciar, 3000);
};
7
Ext.Ajax.request({
url: 'usuario/renovarToken.action',
method :'POST',
success: function(response,action){
var result = Ext.JSON.decode(response.responseText);
if (result.success)
{
store.getAt(0).set('token',result.data.token);
store.getAt(0).set('tiempoRenovacion',result.data.tiempoRenovacion);
clearInterval(intRenovarToken);
intRenovarToken=window.setInterval(ObtenerTokenNuevo,
result.data.tiempoRenovacion);
}
},
failure: function(response,action){
try{
var json = Ext.JSON.decode(response.responseText);
}catch(e){
Ext.Msg.alert('Informacin','Ha ocurrido un error,
detalles: '+json.message);
}
}
});
}
}
catch(e) {
//la cadena no contiene un formato valido json
console.log('Error inesperado: '+e.message);
}
};
8
}
return ExtJSReturn.mapOK(u);
} catch (ErrorWebException e) {
return ExtJSReturn.mapError(e.getMessage(), 10);
} catch (Exception e) {
log.error("validar fallo", e);
return ExtJSReturn.mapError("Error validando usuario");
}
}
/**
*
* @param idUsuario
* @return
*/
@RequestMapping(value = "/renovarToken")
public @ResponseBody
Map<String, Object> renovarToken(HttpServletRequest httpServletRequest) {
try {
String codigo = (String)
httpServletRequest.getAttribute("idUsuario");
int idUsuario = codigo != null && !codigo.equals("") ?
Integer.parseInt(codigo) : -1;
HttpSession session = httpServletRequest.getSession();
UsuarioAutenticacionVO u = (UsuarioAutenticacionVO)
session.getAttribute("usuario");
if (u != null && idUsuario != -1 && u.getCodigo() == idUsuario) {
TokenVO token = new TokenVO();
String tokenNuevo =
Jwts.builder().setSubject(String.valueOf(idUsuario)).signWith(SignatureAlgorithm
.HS256, Constantes.KEY).setIssuedAt(new Date()).setExpiration(new
Date(Calendar.getInstance().getTimeInMillis() +
Constantes.TIEMPO_EXPIRACION_TOKEN)).compact();
token.setToken("Bearer " + tokenNuevo);
token.setTiempoRenovacion(Constantes.TIEMPO_RENOVACION_TOKEN);
return ExtJSReturn.mapOK(token);
} else {
return ExtJSReturn.mapError("Error al renovar token, no se pudo
validar la sesion correctamente.", 666);
}
} catch (Exception e) {
log.error("renovarToken fallo", e);
return ExtJSReturn.mapError("Error al renovar token, no se pudo
validar la sesion correctamente.", 666);
}
}
/**
*
* @param idUsuario
* @return
*/
@RequestMapping(value = "/cerrarSesion")
public @ResponseBody
Map<String, Object> cerrarSesion(HttpServletRequest httpServletRequest) {
try {
9
HttpSession session = httpServletRequest.getSession(false);
if (session != null) {
session.invalidate();
}
return ExtJSReturn.mapOK();
} catch (Exception e) {
log.error("cerrarSesion fallo", e);
return ExtJSReturn.mapError("Error al cerrar sesion.");
}
}
Pruebas
Para probar se capturaron algunas URL, junto con sus parmetros a travs de la
consola del navegador y se utiliz Postman, el cual es una utilidad que se instala
al navegador Chrome y permite realizar peticiones en las que es posible aadir el
token de forma manual.
Consideraciones adicionales
Para hacer que JWT se mucho ms seguro se recomienda:
1. cifrar la informacin si se va incluir ms datos sensibles dentro del payload
del JWT usando la funcin PUT.
2. Trasmitir los token sobre HTTPS para prevenir ataques man-in-the-middle.
3. Asignar un tiempo de expiracin bajo de 20 minutos a 1 hora, y renovarlos
automticamente una sola vez, cuando estos estn prximos a vencer.