摘要:如子異常都可以向上轉型為統一的驗證異常。在設計之初的時候,驗證類統一的父級異常是。兩個場景下的異常類關系圖譜通過上面的圖譜我們便知道了,三個異常都可以向上轉型為。向下轉型調整后的代碼在外層根據不同異常而做不同的業務處理的代碼就可以改造為如下
引言
在使用Spring Security的過程中,我們會發現框架內部按照錯誤及問題出現的場景,劃分出了許許多多的異常,但是在業務調用時一般都會向外拋一個統一的異常出來,為什么要這樣做呢,以及對于拋出來的異常,我們又該如何分場景進行差異化的處理呢,今天來跟我一起看看吧。
Spring Security框架下的一段登錄代碼@PostMapping("/login") public void login(@NotBlank String username, @NotBlank String password, HttpServletRequest request) { try { request.login(username, password); System.out.println("login success"); } catch (ServletException authenticationFailed) { System.out.println("a big exception authenticationFailed"); } }
代碼執行到request.login(username,password)時會跳入到了HttpServlet3RequestFactory類中,點擊去發現login方法只是統一向外拋出了一個ServletException異常。
public void login(String username, String password) throws ServletException { if (this.isAuthenticated()) { throw new ServletException("Cannot perform login for "" + username + "" already authenticated as "" + this.getRemoteUser() + """); } else { AuthenticationManager authManager = HttpServlet3RequestFactory.this.authenticationManager; if (authManager == null) { HttpServlet3RequestFactory.this.logger.debug("authenticationManager is null, so allowing original HttpServletRequest to handle login"); super.login(username, password); } else { Authentication authentication; try { authentication = authManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (AuthenticationException var6) { SecurityContextHolder.clearContext(); throw new ServletException(var6.getMessage(), var6); } SecurityContextHolder.getContext().setAuthentication(authentication); } } }
authenticate()為賬號校驗的主方法,進入到其中的一個實現類ProviderManager中,會發現方法實際拋出是統一拋出的AuthenticationException異常,方法體內實則會出現很多的場景性的異常,如AccountStatusException、InternalAuthenticationServiceException等等。
public Authentication authenticate(Authentication authentication) throws AuthenticationException { Class extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled(); Iterator var6 = this.getProviders().iterator(); while(var6.hasNext()) { AuthenticationProvider provider = (AuthenticationProvider)var6.next(); if (provider.supports(toTest)) { if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { result = provider.authenticate(authentication); if (result != null) { this.copyDetails(authentication, result); break; } } catch (AccountStatusException var11) { this.prepareException(var11, authentication); throw var11; } catch (InternalAuthenticationServiceException var12) { this.prepareException(var12, authentication); throw var12; } catch (AuthenticationException var13) { lastException = var13; } } } if (result == null && this.parent != null) { try { result = this.parent.authenticate(authentication); } catch (ProviderNotFoundException var9) { ; } catch (AuthenticationException var10) { lastException = var10; } } if (result != null) { if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) { ((CredentialsContainer)result).eraseCredentials(); } this.eventPublisher.publishAuthenticationSuccess(result); return result; } else { if (lastException == null) { lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}")); } this.prepareException((AuthenticationException)lastException, authentication); throw lastException; } }多態和向上轉型介紹
這里就涉及到了多態的知識點,異常的多態。如子異常AccountStatusException都可以向上轉型為統一的驗證異常AuthenticationException。
在設計之初的時候,驗證類統一的父級異常是AuthenticationException。然后根據業務需求向下拓展出了很多個場景性質的異常,可能有十個、一百個、一千個。
但是這些具體的場景異常都是從AuthenticationException延伸出來的。
在這個驗證登陸的方法中,會驗證各種場景下登陸是否合法,就有可能出現很多的異常場景,諸如:
密碼不正確 BadCredentialsException
賬號是否被鎖定 LockedException
賬號是否被禁用 DisabledException
賬號是否在有效期內 AccountExpiredException
密碼失效 CredentialsExpiredException
...幾十個幾百個異常,如果每個都需要事無巨細的拋出,那你需要在方法后面寫幾百個異常。
但是你會發現在驗證方法那里統一拋出的是他們的統一父類AuthenticationException,這里用到的就是自動的向上轉型。
到業務層我們拿到AuthenticationException后,需要進行對特定場景下的業務處理,如不同的異常錯誤返回提示不一樣,這個時候就需要用到向下轉型。
Throwable throwable = authenticationFailed.getRootCause(); if (throwable instanceof BadCredentialsException) { do something... }
如果父類引用實際指的是憑證錯誤,則進行密碼錯誤提示。
ServletException和AuthenticationException是兩個框架下的特定場景下的頂級異常,兩個怎么建立聯系呢?
答曰:直接將兩個都統一轉為Throwable可拋出的祖先異常,這樣向下都可以轉成他們自己了,以及各自場景下的所有異常了。
通過上面的圖譜我們便知道了,ServletException、BadCredentialsException、DisabledException三個異常都可以向上轉型為Throwable。
那是在哪里進行的向上類型轉換的呢?
答曰:
public void login(String username, String password) throws ServletException{ something code ... catch (AuthenticationException loginFailed) { SecurityContextHolder.clearContext(); throw new ServletException(loginFailed.getMessage(), loginFailed); } } // 在捕獲到異常之后會構建一個ServletException并將AuthenticationException統一的包裝進去,比如說內部報了BadCredentialsException,那么在這里就會向上轉型為Throwable public ServletException(String message, Throwable rootCause) { super(message, rootCause); } // 在Throwable類中會將最下面冒出來的異常傳給cause,getRootCause就能獲得異常的具體原因 public Throwable(String message, Throwable cause) { fillInStackTrace(); detailMessage = message; this.cause = cause; }
在外層邏輯處理時,針對不同的業務場景我們便可以通過向下類型轉化來完成,如使用instanceof來驗證對象是否是子類的實例。
// Throwable向下轉型BadCredentialsException if (throwable instanceof BadCredentialsException)調整后的代碼
在外層根據不同異常而做不同的業務處理的代碼就可以改造為如下
@PostMapping("/login") public void login(@NotBlank String username, @NotBlank String password, HttpServletRequest request) { try { request.login(username, password); System.out.println("login success"); } catch (ServletException authenticationFailed) { Throwable throwable = authenticationFailed.getRootCause(); if (throwable instanceof BadCredentialsException) { System.out.println("user password is wrong"); }else if (throwable instanceof DisabledException){ System.out.println("user is disabled"); }else { System.out.println("please contact the staff"); } } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72381.html
摘要:如何良好的在代碼中設計異常機制本身設計的出發點是極好的,通過編譯器的強制捕獲,可以明確提醒調用者處理異常情況。但使用此種異常后,該會像病毒一樣,得不到處理后會污染大量代碼,同時也可能因為調用者的不當處理,會失去異常信息。 1、異常是什么? 父類為Throwable,有Error和Exception兩個子類 Error為系統級別的異常(錯誤) Exception下有眾多子類,常見的有Ru...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
摘要:的版本增加了對事件監聽程序的支持,事件監聽程序在建立修改和刪除會話或環境時得到通知。元素指出事件監聽程序類。過濾器配置將一個名字與一個實現接口的類相關聯。 1.簡介 web.xml文件是Java web項目中的一個配置文件,主要用于配置歡迎頁、Filter、Listener、Servlet等,但并不是必須的,一個java web項目沒有web.xml文件照樣能跑起來。Tomcat容器/...
閱讀 3941·2021-11-17 09:33
閱讀 3288·2021-10-08 10:05
閱讀 3117·2021-09-22 15:36
閱讀 1143·2021-09-06 15:02
閱讀 2774·2019-08-29 12:45
閱讀 1594·2019-08-26 13:40
閱讀 3404·2019-08-26 13:37
閱讀 425·2019-08-26 13:37