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