navicat怎么添加check约束
335
2022-12-02
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"); } }
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); } } }
但是在ProviderManager类中的public Authentication authenticate(Authentication authentication) throws AuthenticationException {}
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) {}
如果父类引用实际指的是凭证错误,则进行密码错误提示,这里又有一个骚操作,ServletException和AuthenticationException是两个框架下的顶级父级别的异常,两个怎么建立联系,直接将两个都统一转为Throwable可抛出的祖先异常,这样向下都可以转成他们自己了,以及各自场景下的所有异常了。
两个场景下的异常类关系图谱
ServletException
ServletException可以向上转型为Throwable
BadCredentialsException,密码错误
BadCredentialsException可以向上转型为Throwable
账号被禁用,DisabledException
DisabledException可以向上转型为Throwable
怎么转过去的?
public void login(String username, String password) throws ServletException{...catch (AuthenticationException loginFailed) {SecurityContextHolder.clearContext();throw new ServletException(loginFailed.getMessage(), loginFailed);}}// 在捕获到异常之后会构建一个ServletException并将AuthenticationException统一的包装进去,比如说内部报了BadCredentialsException,那么在这里就会向上转型为Throwablepublic ServletException(String message, Throwable rootCause) { super(message, rootCause);}// 在Throwable类中会将最下面冒出来的异常传给cause,getRootCause就能获得异常的具体原因public Throwable(String message, Throwable cause) { fillInStackTrace(); detailMessage = message; this.cause = cause;}// Throwable向下转型BadCredentialsExceptionif (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"); } } }
作者:翎野君
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~