...
Para comprobar el token en cada petición que se haga, tendremos un filtro de autorización. Un ejemplo, podría ser este:
| Bloque de código | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager
.getLogger( JwtAuthorizationFilter.class );
@Autowired
private ExpedientesService expedientesService;
@Override
protected booleanvoid shouldNotFilterdoFilterInternal( HttpServletRequest request httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain ) throws ServletException, IOException {
finalif List<String>( skipUrlsisPrivate( = Arrays.asList( "/public/**" );httpServletRequest ) ) {
final AntPathMatcherString pathMatchertoken = new AntPathMatcher(httpServletRequest.getHeader( HEADER_AUTHORIZATION_KEY );
return skipUrls.parallelStream().anyMatch( p -> pathMatcher.match( p, request.getServletPath() ) );
}
@Override
protected void doFilterInternal( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain ) throws ServletException, IOException {
// En getSivaIdFromToken se obtienen los datos del token, lanzando las excepciones correspondientes
// si éste es incorrecto o ha expirado
final String tokensivaId = httpServletRequestexpedientesService.getHeadergetSivaIdFromToken( HEADER_AUTHORIZATION_KEYtoken );
// EnSi getSivaIdFromTokenno sehay obtienensivaId loso datos del token, lanzando las excepciones correspondientes si éste es
// incorrecto o ha expirado
final String sivaId = expedientesService.getSivaIdFromToken( token );
// Si no hay sivaId o es -1, sees -1, se lanza una excepción
if ( ( sivaId == null ) || sivaId.contentEquals( "-1" ) ) {
throw new UnauthorizedException();
}
}
filterChain.doFilter( httpServletRequest, httpServletResponse );
}
} |
Como vemos, definimos dos métodos:
...
private boolean isPrivate( HttpServletRequest httpServletRequest ) {
final RequestMatcher privateUrl = new AntPathRequestMatcher( PRIVATE_PREFIX );
return privateUrl.matches( httpServletRequest );
}
} |
...
De aquí, lo más importante, y lo que tendremos que implementar de acuerdo a las necesidades de nuestra aplicación es el método doFilterInternal(). En éste, inicialmente se comprueba que la petición es a un método privado (las demás no se filtrarán) con la función isPrivate definida más abajo. Después va la parte personalizada, en este caso se comprueba el token y se obtiene el siva id; ambas cosas están implementadas en la llamada a getSivaIdFromToken de expedientesService, que tenemos en nuestro proyecto y hemos añadido con @Autowired, de forma que si cualquiera de los dos pasos falla, se lanzará una excepción, que es la que se devolverá al frontend y será controlada en éste.
Si todo es correcto, llamamos a filterChain.doFilter(...), que sirve para continuar al siguiente filtro, si hay otros definidos.
En el caso de que utilicemos login con cliente CAS, también incluiremos un filtro de autenticación, que verificará la identidad del usuario que realiza las peticiones. Tenemos el siguiente ejemplo:
...
Podemos ver que además del constructor, implementamos los métodos attempAuthentication y successfulAuthentication en los que intentamos autenticar al usuario con sus credenciales y especificamos el comportamiento una vez se ha hecho la autenticación, respectivamente.
| Atención: en las aplicaciones con login en el Portal de Servicios, falta por implementar la verificación del usuario que realiza las peticiones, en cuanto esté implementado lo añadiremos a esta guía. |
|---|
Estos Este filtros tendremos que añadirlos añadirlo en nuestra configuración de seguridad:
| Bloque de código | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
/** * Defino como Bean el authorization filter para que sea accedido desde los filtros ya que no estan en el mismo contexto */ @Bean public JwtAuthorizationFilter jwtAuthorizationFilterBean() { return new JwtAuthorizationFilter(); } /** * Proveedor de gestion de acceoss */ @Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ) // configuro politica de // sesion sin estado .and().cors() // Aniado configuracion CORS por defecto /** * Defino como Bean el authentication manager bean para que sea accedido desde los filtros ya que no estan en el mismo contexto */ @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public JwtAuthorizationFilter jwtAuthorizationFilterBean() { return new JwtAuthorizationFilter(); } /** * Proveedor de gestion de acceoss */ @Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // configuro politica de sesion sin estado .and().cors() // Aniado configuracion CORS por defecto .and().csrf().disable() // Deshabilito csrf ya que exponemos el api a cualquier dominio .authorizeRequests().antMatchers( "/public/**" ).permitAll() // Permito pasar sin autenticar todas las // peticiones que vayan a "/public" .anyRequest().authenticated() // El resto de peticiones deben ser autenticadas; //Filtro de atenticacion //Primer filtro intercepta peticiones de login .and().addFilterBefore(new JwtAuthenticationFilter(authenticationManagerBean()),UsernamePasswordAuthenticationFilter.class) //Segundo filtro valida el tokencsrf().disable() .addFilterBefore( jwtAuthorizationFilterBean(), UsernamePasswordAuthenticationFilterBasicAuthenticationFilter.class ); } |
Como vemos, en el configure incluimos que a las rutas /public se permitan todas, lo que permite hacer llamadas a esos métodos sin necesidad de autenticar, es decir, en esas llamadas no se aplicará el filtro de autenticación. Para hacer lo mismo en el filtro de autorización, ya hemos visto que disponemos de un método en el propio filtrotenemos sólo filtro de autorización, pero no tenemos filtro de autenticación. Esto se debe a que La autenticación es realizada por el Portal de Servicios en el proceso de obtención y refresco del token, por lo que no es necesario en nuestros servicios.
Obtener datos del token
Para obtener los datos del token, podemos obtener sus Claims, que contienen la información que se pasa en el token. Se utiliza el secret para descifrarlo. Para esto tenemos una clase JwtTokenUtil que contiene el método getClaim, que devuelve un objeto Claims o lanza una excepción si el token esta vacío, es incorrecto o ha expirado:
...