作者:青枫望 | 来源:互联网 | 2023-09-17 12:55
一、前言:
在 Spring Security 框架中,最常用的 Filter 便是表单登录Filter,即 UsernamePasswordAuthenticationFilter。从下图中,能清晰的了解到 UsernamePasswordAuthenticationFilter 的继承关系。
最近我用 Spring Security 做登录验证的时候遇到一个问题。UsernamePasswordAuthenticationFilter 继承的子类重写successfulAuthentication 、unsuccessfulAuthentication 后,发现登录成功会调用 successfulAuthentication 方法, 然而登录失败却不会调用 unsuccessfulAuthentication 方法, 而是直接抛出异常:
org.springframework.security.authentication.BadCredentialsException: Bad credentials
二、部分代码:
UsernamePasswordAuthenticationFilter 的继承子类:认证过滤器,如果用户名和密码正确,那么过滤器将创建一个JWT Token 并在 HTTP Response 的 header 中返回它,格式:token: “Bearer +具体token值”, 反之返回登录失败。
二、问题分析及解决:
查看 Spring Security 账号密码验证这一块的源码:DaoAuthenticationProvider.java
从上面源码可以看出当用户输入的密码与数据库查询出的密码不匹配时,JVM 会立即终止当前线程的运行,并向上一层(ProviderManager.java)抛出异常。
异常最后会在 ProviderManager.java 类中被抛出:
最终被我的 try {} catch (Exception e) {} 捕获处理,将异常打印到控制台,并且 return null
程序直接结束了,并没有调用 unsuccessfulAuthentication 方法。我打算跟着正确的账号密码断点走一遍看下最后会在哪里调用 successfulAuthentication 方法,得知在 AbstractAuthenticationProcessingFilter.java 抽象类中被调用,最终执行子类 JwtAuthenticationFilter 重写的 successfulAuthentication 方法。并且发现 unsuccessfulAuthentication 方法就在上面,是通过捕获 InternalAuthenticationServiceException | AuthenticationException 异常来调用执行。
那我就在想,是不是我外层自定义的 try {} catch (Exception e) {} 把异常捕获并 return null,导致异常没有抛到 AbstractAuthenticationProcessingFilter.java 类中去? 我试着去掉 return null, 发现问题得以解决。其实这里并不需要加 try catch 处理异常,这里就是需要异常作为依据,不要去做任何操作!