spring - example - Haga que el trabajo de filtro de servlets sea simple con @ControllerAdvice
spring security ejemplo (2)
Tengo un filtro simple solo para verificar si una solicitud contiene un encabezado especial con clave estática, sin autenticación de usuario, solo para proteger los puntos finales. La idea es arrojar una AccessForbiddenException
si la clave no coincide, que luego se asignará a la respuesta con una clase anotada con @ControllerAdvice
. Sin embargo, no puedo hacer que funcione. Mi @ExceptionHandler
no es llamado.
ClientKeyFilter
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Controller
import javax.servlet.*
import javax.servlet.http.HttpServletRequest
@Controller //I know that @Component might be here
public class ClientKeyFilter implements Filter {
@Value(''${CLIENT_KEY}'')
String clientKey
public void init(FilterConfig filterConfig) {}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
req = (HttpServletRequest) req
def reqClientKey = req.getHeader(''Client-Key'')
if (!clientKey.equals(reqClientKey)) {
throw new AccessForbiddenException(''Invalid API key'')
}
chain.doFilter(req, res)
}
public void destroy() {}
}
AccessForbiddenException
public class AccessForbiddenException extends RuntimeException {
AccessForbiddenException(String message) {
super(message)
}
}
ExceptionController
@ControllerAdvice
class ExceptionController {
static final Logger logger = LoggerFactory.getLogger(ExceptionController)
@ExceptionHandler(AccessForbiddenException)
public ResponseEntity handleException(HttpServletRequest request, AccessForbiddenException e) {
logger.error(''Caught exception.'', e)
return new ResponseEntity<>(e.getMessage(), I_AM_A_TEAPOT)
}
}
Donde estoy equivocado? ¿Puede el filtro de servlet simple funcionar con el mapeo de excepción de Spring-boot?
No puede usar @ControllerAdvice
, porque se llama en caso de una excepción en algún controlador, pero su ClientKeyFilter
no es un @Controller
.
Debe reemplazar la anotación @Controller
con el @Component
y simplemente establecer el cuerpo y el estado de la respuesta de esta manera:
@Component
public class ClientKeyFilter implements Filter {
@Value(''${CLIENT_KEY}'')
String clientKey
public void init(FilterConfig filterConfig) {
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String reqClientKey = request.getHeader("Client-Key");
if (!clientKey.equals(reqClientKey)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid API key");
return;
}
chain.doFilter(req, res);
}
public void destroy() {
}
}
Según lo especificado por la especificación del servlet de java, los Filter
s se ejecutan siempre antes de que se invoque un Servlet
. Ahora, un @ControllerAdvice
solo es útil para el controlador, que se ejecuta dentro del DispatcherServlet
. Así que usar un Filter
y esperar un @ControllerAdvice
o en este caso el @ExceptionHandler
, que se invocará no va a suceder.
HandlerInterceptor
colocar la misma lógica en el filtro (para escribir una respuesta JSON) o en lugar de un filtro usar un HandlerInterceptor
que hace esta comprobación. La forma más fácil es extender HandlerInterceptorAdapter
e invalidar e implementar el método preHandle
y poner la lógica del filtro en ese método.
public class ClientKeyInterceptor extends HandlerInterceptorAdapter {
@Value(''${CLIENT_KEY}'')
String clientKey
@Override
public boolean preHandle(ServletRequest req, ServletResponse res, Object handler) {
String reqClientKey = req.getHeader(''Client-Key'')
if (!clientKey.equals(reqClientKey)) {
throw new AccessForbiddenException(''Invalid API key'')
}
return true;
}
}