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);
}
}
}
但是在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。然后根据业务需求向下拓展出了很多个场景性质的异常,可能有十个、一百个、一千个。
- 密码不正确 BadCredentialsException
- 账号是否被锁定 LockedException
- 账号是否被禁用 DisabledException
- 账号是否在有效期内 AccountExpiredException
- 密码失效 CredentialsExpiredException
两个场景下的异常类关系图谱
ServletException

BadCredentialsException,密码错误

账号被禁用,DisabledException

public void login(String username, String password) throws ServletException{
...
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;
}
// 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");
}
}
}
Spring Security中异常上抛机制及对于转型处理的一些感悟的更多相关文章
- Spring Security 中的过滤器
本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12. Spring Security 的本质是一个过滤器链(filter chain) ...
- [收藏]Spring Security中的ACL
ACL即访问控制列表(Access Controller List),它是用来做细粒度权限控制所用的一种权限模型.对ACL最简单的描述就是两个业务员,每个人只能查看操作自己签的合同,而不能看到对方的合 ...
- Spring Security中实现微信网页授权
微信公众号提供了微信支付.微信优惠券.微信H5红包.微信红包封面等等促销工具来帮助我们的应用拉新保活.但是这些福利要想正确地发放到用户的手里就必须拿到用户特定的(微信应用)微信标识openid甚至是用 ...
- java中异常的抛出:throw throws
java中异常的抛出:throw throws Java中的异常抛出 语法: public class ExceptionTest{ public void 方法名(参数列表) throws 异常列表 ...
- Spring Security 中的 Bcrypt
最近在写用户管理相关的微服务,其中比较重要的问题是如何保存用户的密码,加盐哈希是一种常见的做法.知乎上有个问题大家可以先读一下: 加盐密码保存的最通用方法是? 对于每个用户的密码,都应该使用独一无二的 ...
- 看源码,重新审视Spring Security中的角色(roles)是怎么回事
在网上看见不少的博客.技术文章,发现大家对于Spring Security中的角色(roles)存在较大的误解,最大的误解就是没有搞清楚其中角色和权限的差别(好多人在学习Spring Security ...
- 六:Spring Security 中使用 JWT
Spring Security 中使用 JWT 1.无状态登录 1.1 什么是有状态? 1.2 什么是无状态 1.3 如何实现无状态 2.JWT 2.1 JWT数据格式 2.2 JWT交互流程 2.3 ...
- 五:Spring Security 中的角色继承问题
Spring Security 中的角色继承问题 以前的写法 现在的写法 源码分析 SpringSecurity 在角色继承上有两种不同的写法,在 Spring Boot2.0.8(对应 Spring ...
- Spring Security中html页面设置hasRole无效的问题
Spring Security中html页面设置hasRole无效的问题 一.前言 学了几天的spring Security,偶然发现的hasRole和hasAnyAuthority的区别.当然,可能 ...
随机推荐
- sencha touch list(列表) item(单行)单击事件触发顺序
测试代码如下 Ext.define('app.view.new.List', { alternateClassName: 'newList', extend: 'app.view.util.MyLis ...
- 使用 udev 管理 Linux 设备文件
本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本文会使那 ...
- Windows Phone Bing lock screen doesn't change解决方法
之前一直用的Lumia 925,Bing lock screen每天都会更换.这几天换了Lumia 930,同步了账号相关的设置,发现Bing lock screen不再每天更换.尝试重启.使用cel ...
- 23种设计模式之代理模式(Proxy)
代理模式是一种对象结构型模式,可为某个对象提供一个代理,并由代理对象控制对原对象的引用.代理模式能够协调调用者和被调用者,能够在一定程度上降低系统的耦合度,其缺点是请求的处理速度会变慢,并且实现代理模 ...
- 【BZOJ1210】[HNOI2004]邮递员 插头DP+高精度
[BZOJ1210][HNOI2004]邮递员 Description Smith在P市的邮政局工作,他每天的工作是从邮局出发,到自己所管辖的所有邮筒取信件,然后带回邮局.他所管辖的邮筒非常巧地排成了 ...
- 21ic编辑推荐:从单片机开始的程序运行
一直不清楚单片机中程序的执行过程,就是知道一个程序总是从一个main函数开始执行,然后把程序段存放在ROM里面,动态数据存放在RAM里面,而单片机的RAM资源又是及其的稀少,所以要省着用,但是到底怎么 ...
- 7.24python协程(2)和IO模型
2018-7-24 08:50:29 异步IO模型 epoll 机制 linux 给每个监听对象绑定回调函数,当要读的对象来了时候,回调函数直接被执行,然后通知用户,效率非常高! python无法 ...
- POJ 3067 - Japan - [归并排序/树状数组(BIT)求逆序对]
Time Limit: 1000MS Memory Limit: 65536K Description Japan plans to welcome the ACM ICPC World Finals ...
- JavaScript学习12.1
JavaScript弹窗可以创建3种消息框:警告框.确认框.提示框,可以不带window对象直接使用相应的方法警告框:保护用户可以得到某些信息,当出现警告框后需要用户点击确认按钮之后才能操作windo ...
- 通过微信服务号推送Zabbix告警
近期看到一篇通过微信实现Zabbix告警的文章,但实践时发现,无法成功发送消息. 分析原因,应该是微信公众平台加强了登录验证,在登录时会需要管理员进行扫描二维码操作才能成功登陆后台: 而之前文章中的A ...