JwtRequestFilter.java
/**
* TFG 75.678 - TFG Desarrollo web 2020 e-Learning for Schools
* Copyright (C) 2020 Eduardo Rodriguez Carro
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.uoc.tfg.sel.security;
import java.io.IOException;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.uoc.tfg.sel.security.model.JWTToken;
import org.uoc.tfg.sel.security.model.UserDetailsExtended;
import org.uoc.tfg.sel.service.SessionService;
import io.jsonwebtoken.ExpiredJwtException;
/**
* Filtro de las peticiones para inyectar el usuario si obtenemos un Token
* Valido.
*
* @author Eduardo Rodriguez Carro
*/
@Profile("!test")
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
/** Logger. */
private static final Logger logger = LoggerFactory.getLogger(JwtRequestFilter.class);
/** Servicio de datos de usuario de Spring Security. */
@Autowired
private UserDetailsService userDetailsService;
/** The session service. */
@Autowired
private SessionService sessionService;
/** Utilidades de JWT. */
@Autowired
private JwtUtil jwtTokenUtil;
/**
* Filtro de la peticion.
*
* @param request the request
* @param response the response
* @param chain the chain
* @throws ServletException the servlet exception
* @throws IOException Signals that an I/O exception has occurred.
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
JWTToken token = null;
String username = null;
// JWT Token "Bearer token"
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
try {
String jwtTokenStr = requestTokenHeader.substring(7);
token = jwtTokenUtil.getToken(jwtTokenStr);
username = token.getSubject();
// En caso de error dejamos un log pero no lanzamos excepcion
// Mas adelante el filtro de securidad de spring lo tratara como un problema
} catch (IllegalArgumentException e) {
logger.warn("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
logger.warn("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Si tenemos token pero no tenemos aun contexto de autenticacion
// Esto evita llamar al filtro varias veces en el mismo contexto de peticion
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
String sessionId = token.getSessionId();
// Si el token es valido para con el usuario y tenemos usuario, entonces procedemos a crear un objeto de autenticacion de spring
// con todos sus permisos para poder aplicarlos sobre las API
if (userDetails != null && validateToken(token, userDetails) && sessionService.hasSession(sessionId) ){
if(userDetails instanceof UserDetailsExtended) {
((UserDetailsExtended)userDetails).setSessionId(sessionId);
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// Paso del contexto de autenticacion a Spring security para acceder a las APIs protegidas por ruta (WebSecurityConfig) y
// a las anotadas con un rol especifico
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
/**
* Validacion del token.
*
* @param token the token
* @param userDetails the user details
* @return the boolean
*/
public Boolean validateToken(JWTToken token, UserDetails userDetails) {
// ha de ser para este usuario
// ha de tener una fecha de expiracion posterior
return token.getSubject().equals(userDetails.getUsername()) && !token.getExpiration().before(new Date());
}
}