Spring Security 异常没有抛出去的问题
记录一个使用Spring Security自定义异常时出现的问题。
UserDetailsService returned null, which is an interface contract violation
环境描述
定义了一个登录异常,继承security里的Authentication异常
import org.springframework.security.core.AuthenticationException;
public class LoginException extends AuthenticationException {
public LoginException(String msg, Throwable t) {
super(msg, t);
}
public LoginException(String msg) {
super(msg);
}
}
在AuthenticationProvider里校检,并抛出异常
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
H5AuthenticationToken token = (H5AuthenticationToken) authentication;
String username = (String) token.getPrincipal();
String password = (String) token.getCredentials();
int type = token.getType() == null ? 0 : token.getType();
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
throw new LoginException(String.format("用户名或%s不能为空!", type == 0 ? "验证码" : "密码"));
}
//这里省略项目里的加载用户代码
...
}
定义登录失败处理类
public class LoginAuthFailHandler extends AbstractResponseHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.setContentType("application/json; charset=utf-8");
if(exception instanceof AuthenticationServiceException){
String message = exception.getMessage();
response.getWriter().write(Result.fail(10001,message).toString());
}
}
}
用postman测试接口,故意不输入password,让if语句执行为true,抛出异常。结果并没有返回我自定义的错误信息,而是返回以下信息
{
"code": 10001,
"msg": "UserDetailsService returned null, which is an interface contract violation"
}
控制台报错
org.springframework.security.authentication.InternalAuthenticationServiceException: UserDetailsService returned null, which is an interface contract violation
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:110) ~[spring-security-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144) ~[spring-security-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
//以下省略
...
Debug查找原因
经过一番排查,发现问题出现在security框架里的ProviderManager类里,捕捉自定义异常后没有直接抛出,而是保存了异常,然后因为出现了新的异常而将自定义的异常覆盖了
)]
解决方案
将LoginException类,从继承AuthenticationException更改为继承InternalAuthenticationServiceException
public class LoginException extends InternalAuthenticationServiceException {
public LoginException(String msg, Throwable t) {
super(msg, t);
}
public LoginException(String msg) {
super(msg);
}
}
使ProviderManager类里捕捉到自定义异常时直接抛出
结果
成功解决