簡體   English   中英

Spring REST / MVC&Security異常處理和過濾器鏈要求

[英]Spring REST/MVC & Security exception handling & filter chain requirements

我有一個使用Spring Security的Spring REST應用程序。 到目前為止,我一直在使用@ControllerAdvice類進行錯誤處理,該類非常適合處理MVC過程中拋出的所有錯誤。

但是,Spring Security拋出的所有異常(例如: AccessDeniedException )都不是由MVC錯誤處理處理的,因為它們實際上從未實際進入MVC組件。 因此,如果我想處理Spring Security異常,我必須添加AccessDeniedHandler或使用在EXCEPTION_TRANSLATION_FILTER之后放置的自定義過濾器(請參閱Spring Docs和Filter Ordering )。

我處理異常的主要目標是,無論錯誤是否被拋入JSON對象中嵌入的相關信息(例如:時間戳,錯誤代碼,消息等),我都希望向客戶端返回一致的響應。

有這種設置的首選技術嗎? 或者是一種能夠集中一切的更好方法? 我喜歡使用@ExceptionHandler進行MVC錯誤處理的簡單性,但是一切似乎都促使我使用自定義過濾器並在一個位置處理所有內容。

在相關的說明中,在我看來,使用命名空間配置生成的默認FilterChain對於REST應用程序來說是過度的。 是否有更適合REST應用程序的配置? (例如:不需要FORM_LOGIN_FILTER,REMEMBER_ME_FILTER,CONCURRENT_SESSION_FILTER等......)。 有沒有人有一個應該在REST應用程序中使用的過濾器列表? 我在文檔中找不到任何內容。

我不確定我是否理解你。 我還有一個問題是將json發送回客戶端的正確錯誤消息。 我編寫了所有(過濾器和提供程序)自定義,因為我通過HTTPHeaderFields進行身份驗證。 錯誤處理和向客戶端發送正確的消息是最大的問題。 簡短概述如下:

起初我創建了自定義delegatingAuthenticationEntryPoint和AccesssDeniedHandler。
Snipp的安全配置

...
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            // IMPORTANT: Add Filter after "ExceptionTranslation". 
            // If not AuthenticationException from Custom Filter or Custom Provider
            // will not be catched by AuthenticationEntryPoint.
            .addFilterAfter(httpClientFilter(), ExceptionTranslationFilter.class)
            .exceptionHandling()
                // catch AuthenticationExeption and SecureToken with authenticated=false
                .authenticationEntryPoint(delegatingAuthenticationEntryPoint())
                // catch PermissionDenied Exeption e.g. missing in authorizeRequests()
                .accessDeniedHandler(new ClientRestAccessDeniedHandler())
                .and()
...

ClientRestAccessDeniedHandler看起來像這樣

public class ClientRestAccessDeniedHandler implements AccessDeniedHandler{

    @Override
    public void handle(HttpServletRequest request,  HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {

        final Logger logger = Logger.getLogger(ClientRestAccessDeniedHandler.class);

        if(logger.isDebugEnabled())
            logger.debug("Requered Role for this request is missing!");

        HTTPAuthenticationErrorSender.sendResponse(request, response, 
                SecurityContextHolder.getContext().getAuthentication());
    }
}

Custom DelegationEntriyPoint看起來非常相似。 兩者都調用HTTPAuthenticationErrorSender。 它將發送由低級別東西生成的HttpResponse :-)。

public final class HTTPAuthenticationErrorSender {

    public static void sendResponse(HttpServletRequest request, HttpServletResponse response, Authentication token) 
            throws JsonGenerationException, JsonMappingException, IOException{

        final Logger logger = Logger.getLogger(HTTPAuthenticationErrorSender.class);

        if(!(token instanceof HTTPRestSecureToken)){
            if (token != null){
                response.sendError(403, "No valide AuthenticationToken found. Token instance of: "+token.getClass().toString());
                if(logger.isDebugEnabled())
                    logger.debug("Send default HTTP Response 403. No HTTPRestSecureToken found. "
                            + "Token is instance of: "+token.getClass().getName());
            }
            else {
                response.sendError(403, "No valide AuthenticationToken found. Token is null");
                if(logger.isDebugEnabled())
                    logger.debug("Send default HTTP Response 403. No HTTPRestSecureToken found. "
                            + "Token is null");
            }
            return;
        }

        HTTPRestSecureToken restToken = (HTTPRestSecureToken) token;
        ObjectMapper mapper = new ObjectMapper();
        AuthenticationErrorResponse authErrorResponse =
                new AuthenticationErrorResponse(restToken.getAuthStatus().getErrorCode(),restToken.getAuthStatus().getDescription());
        String content = mapper.writeValueAsString(authErrorResponse);

        HTTPRestPrincipal principal = (HTTPRestPrincipal) token.getPrincipal();

        if(logger.isDebugEnabled()){
            logger.debug("AccessDenied for request: ["+principal.getFullURI()+"] clientID: ["+principal.getClientID()
                    + "] loginMail: ["+principal.getLoginMail()+"]");
            logger.debug("Send following json response: "+content);
        }

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().print(content);
    }

}

要發送正確的信息,我會在自定義SecureToken中跟蹤狀態信息。 令牌將運行拋出安全鏈過濾器和提供程序,並將填充信息。

是的,我會說這對於像這樣的常見問題並不是很小。 但我找不到另一種方式。 默認的Impl。 對我的情況不夠詳細。 我希望它會有所幫助,或者讓你朝着正確的方向前進。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM