摘要:如子異常都可以向上轉型為統(tǒng)一的驗證異常。在設計之初的時候,驗證類統(tǒng)一的父級異常是。兩個場景下的異常類關系圖譜通過上面的圖譜我們便知道了,三個異常都可以向上轉型為。向下轉型調整后的代碼在外層根據(jù)不同異常而做不同的業(yè)務處理的代碼就可以改造為如下
引言
在使用Spring Security的過程中,我們會發(fā)現(xiàn)框架內部按照錯誤及問題出現(xiàn)的場景,劃分出了許許多多的異常,但是在業(yè)務調用時一般都會向外拋一個統(tǒng)一的異常出來,為什么要這樣做呢,以及對于拋出來的異常,我們又該如何分場景進行差異化的處理呢,今天來跟我一起看看吧。
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"); } }
代碼執(zhí)行到request.login(username,password)時會跳入到了HttpServlet3RequestFactory類中,點擊去發(fā)現(xiàn)login方法只是統(tǒng)一向外拋出了一個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()為賬號校驗的主方法,進入到其中的一個實現(xiàn)類ProviderManager中,會發(fā)現(xiàn)方法實際拋出是統(tǒng)一拋出的AuthenticationException異常,方法體內實則會出現(xiàn)很多的場景性的異常,如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; } }多態(tài)和向上轉型介紹
這里就涉及到了多態(tài)的知識點,異常的多態(tài)。如子異常AccountStatusException都可以向上轉型為統(tǒng)一的驗證異常AuthenticationException。
在設計之初的時候,驗證類統(tǒng)一的父級異常是AuthenticationException。然后根據(jù)業(yè)務需求向下拓展出了很多個場景性質的異常,可能有十個、一百個、一千個。
但是這些具體的場景異常都是從AuthenticationException延伸出來的。
在這個驗證登陸的方法中,會驗證各種場景下登陸是否合法,就有可能出現(xiàn)很多的異常場景,諸如:
密碼不正確 BadCredentialsException
賬號是否被鎖定 LockedException
賬號是否被禁用 DisabledException
賬號是否在有效期內 AccountExpiredException
密碼失效 CredentialsExpiredException
...幾十個幾百個異常,如果每個都需要事無巨細的拋出,那你需要在方法后面寫幾百個異常。
但是你會發(fā)現(xiàn)在驗證方法那里統(tǒng)一拋出的是他們的統(tǒng)一父類AuthenticationException,這里用到的就是自動的向上轉型。
到業(yè)務層我們拿到AuthenticationException后,需要進行對特定場景下的業(yè)務處理,如不同的異常錯誤返回提示不一樣,這個時候就需要用到向下轉型。
Throwable throwable = authenticationFailed.getRootCause(); if (throwable instanceof BadCredentialsException) { do something... }
如果父類引用實際指的是憑證錯誤,則進行密碼錯誤提示。
ServletException和AuthenticationException是兩個框架下的特定場景下的頂級異常,兩個怎么建立聯(lián)系呢?
答曰:直接將兩個都統(tǒng)一轉為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統(tǒng)一的包裝進去,比如說內部報了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; }
在外層邏輯處理時,針對不同的業(yè)務場景我們便可以通過向下類型轉化來完成,如使用instanceof來驗證對象是否是子類的實例。
// Throwable向下轉型BadCredentialsException if (throwable instanceof BadCredentialsException)調整后的代碼
在外層根據(jù)不同異常而做不同的業(yè)務處理的代碼就可以改造為如下
@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"); } } }
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/72381.html
摘要:請注意,我們在聊聊單元測試遇到問題多思考多查閱多驗證,方能有所得,再勤快點樂于分享,才能寫出好文章。單元測試是指對軟件中的最小可測試單元進行檢查和驗證。 JAVA容器-自問自答學HashMap 這次我和大家一起學習HashMap,HashMap我們在工作中經(jīng)常會使用,而且面試中也很頻繁會問到,因為它里面蘊含著很多知識點,可以很好的考察個人基礎。但一個這么重要的東西,我為什么沒有在一開始...
摘要:如何良好的在代碼中設計異常機制本身設計的出發(fā)點是極好的,通過編譯器的強制捕獲,可以明確提醒調用者處理異常情況。但使用此種異常后,該會像病毒一樣,得不到處理后會污染大量代碼,同時也可能因為調用者的不當處理,會失去異常信息。 1、異常是什么? 父類為Throwable,有Error和Exception兩個子類 Error為系統(tǒng)級別的異常(錯誤) Exception下有眾多子類,常見的有Ru...
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區(qū)分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數(shù)據(jù)結構與算法 應用模塊: 常用工具集 ...
摘要:的版本增加了對事件監(jiān)聽程序的支持,事件監(jiān)聽程序在建立修改和刪除會話或環(huán)境時得到通知。元素指出事件監(jiān)聽程序類。過濾器配置將一個名字與一個實現(xiàn)接口的類相關聯(lián)。 1.簡介 web.xml文件是Java web項目中的一個配置文件,主要用于配置歡迎頁、Filter、Listener、Servlet等,但并不是必須的,一個java web項目沒有web.xml文件照樣能跑起來。Tomcat容器/...
閱讀 4153·2021-11-17 09:33
閱讀 3345·2021-10-08 10:05
閱讀 3263·2021-09-22 15:36
閱讀 1230·2021-09-06 15:02
閱讀 2834·2019-08-29 12:45
閱讀 1648·2019-08-26 13:40
閱讀 3498·2019-08-26 13:37
閱讀 491·2019-08-26 13:37