6

我有一個簡單的過濾器來檢查請求是否包含帶有靜態鍵的特殊標頭 - 沒有用戶身份驗證 - 僅用於保護端點。這個想法是如果密鑰不匹配,則會拋出AccessForbiddenException,然後將映射到帶有註解爲@ControllerAdvice的類的響應。然而,我無法讓它工作。我的@ExceptionHandler未被調用。使用@ControllerAdvice創建簡單的servlet過濾器

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) 
    } 
} 

我錯在哪裏?簡單的servlet過濾器可以使用spring-boot的異常映射嗎?

+0

這絕不會發生過濾器。 '@ ControllerAdvice'只對請求到達'DispatcherServlet'有用,'Filter's總是在那之前執行。要麼把這個邏輯放在過濾器中,要麼代替過濾器使用'HandlerInterceptor'。 –

+0

@ M.Deinum,我終於使用了'HandlerInterceptor'。如果您想將其添加爲答案,我會很樂意接受它。 – Opal

回答

3

正如Java Servlet規範調用一個ServletFilter總是執行規定。現在,@ControllerAdvice僅適用於在DispatcherServlet內部執行的控制器。因此使用Filter並且期望@ControllerAdvice或在這種情況下@ExceptionHandler被調用是不會發生的。

您需要將相同的邏輯放入過濾器(用於寫入JSON響應),或者使用HandlerInterceptor進行檢查,而不是使用過濾器。最簡單的方法是擴展HandlerInterceptorAdapter,只是覆蓋並實現preHandle方法,並將過濾器中的邏輯放入該方法中。

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; 
    } 

} 
+0

可能是因爲您發佈了您的答案而發生了變化,但現在的簽名是: '公共布爾preHandle(HttpServletRequest請求,HttpServletResponse響應,對象處理程序)拋出異常' – Zakaria

4

您不能使用@ControllerAdvice,因爲它在某些控制器出現異常時被調用,但您的ClientKeyFilter不是@Controller

此時應更換@Controller註釋與@Component和剛剛成立的反應和身體狀況是這樣的:

@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() { 
    } 
}