ExceptionTranslationFilter過濾器對應(yīng)的類路徑為 org.springframework.security.web.access.ExceptionTranslationFilter 從類名就看出這個過濾器用于異常翻譯的,。但是從這個過濾器在filterchain中的位置來看,它僅僅處于倒數(shù)第三的位置(這個filter后面分為是FilterSecurityInterceptor,、SwitchUserFilter),,所以ExceptionTranslationFilter只能捕獲到后面兩個過濾器所拋出的異常,。 這里需要強調(diào)一下,,spring security中的異常類基本上都繼承RuntimeException。
接著看ExceptionTranslationFilter執(zhí)行過程
-
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) req;
- HttpServletResponse response = (HttpServletResponse) res;
-
- try {
- chain.doFilter(request, response);
- }
- catch (IOException ex) {
- throw ex;
- }
- catch (Exception ex) {
-
- Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
- RuntimeException ase = (AuthenticationException)
- throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
-
- if (ase == null) {
- ase = (AccessDeniedException)throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
- }
-
- if (ase != null) {
- handleException(request, response, chain, ase);
- } else {
-
-
- if (ex instanceof ServletException) {
- throw (ServletException) ex;
- }
- else if (ex instanceof RuntimeException) {
- throw (RuntimeException) ex;
- }
- throw new RuntimeException(ex);
- }
- }
- }
-
- private void handleException(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
- RuntimeException exception) throws IOException, ServletException {
-
- if (exception instanceof AuthenticationException) {
- sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
- }
-
- else if (exception instanceof AccessDeniedException) {
- if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
- sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException(
- "Full authentication is required to access this resource"));
- }
- else {
- accessDeniedHandler.handle(request, response, (AccessDeniedException) exception);
- }
- }
- }
先分析如何處理認證異常
-
- protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
- AuthenticationException reason) throws ServletException, IOException {
-
-
-
- SecurityContextHolder.getContext().setAuthentication(null);
-
- requestCache.saveRequest(request, response);
- logger.debug("Calling Authentication entry point.");
-
- authenticationEntryPoint.commence(request, response, reason);
- }
這里補充一下 authenticationEntryPoint是由配置http標(biāo)簽時,,通過什么認證入口來決定注入相應(yīng)的入口點bean的,。請看下面的對應(yīng)關(guān)系列表 form-login認證:LoginUrlAuthenticationEntryPoint http-basic認證:BasicAuthenticationEntryPoint openid-login認證:LoginUrlAuthenticationEntryPoint x509認證:Http403ForbiddenEntryPoint
就不一一分析每個EntryPoint了,,著重看一下LoginUrlAuthenticationEntryPoint
-
-
- public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
- throws IOException, ServletException {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
-
- String redirectUrl = null;
-
- if (useForward) {
- if (forceHttps && "http".equals(request.getScheme())) {
- redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest);
- }
-
- if (redirectUrl == null) {
- String loginForm = determineUrlToUseForThisRequest(httpRequest, httpResponse, authException);
- RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginForm);
- dispatcher.forward(request, response);
- return;
- }
- } else {
-
- redirectUrl = buildRedirectUrlToLoginPage(httpRequest, httpResponse, authException);
-
- }
- redirectStrategy.sendRedirect(httpRequest, httpResponse, redirectUrl);
- }
接著分析訪問拒絕類異常的處理過程,看AccessDeniedHandlerImpl的handle方法
- public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
- throws IOException, ServletException {
- if (!response.isCommitted()) {
-
- if (errorPage != null) {
-
- request.setAttribute(SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, accessDeniedException);
-
-
- response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-
-
- RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
- dispatcher.forward(request, response);
-
- } else {
- response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
- }
- }
- }
通過以上分析,可以大體上認識到ExceptionTranslationFilter主要攔截兩類安全異常:認證異常,、訪問拒絕異常,。而且僅僅是捕獲FilterSecurityInterceptor,、SwitchUserFilter以及自定義攔截器的異常,。所以在自定義攔截器時,,需要注意在鏈中的順序。
在上面分析過程中,,有requestCache.saveRequest(request, response);的語句,,具體requestCache的用途下篇分析。
|