自定义Spring Security认证管理器未抛出空凭证异常问题
空凭证未触发BadCredentialsException的原因及解决方法
核心原因
你的代码仅校验了凭证是否为null,但未处理空字符串场景:
- 当前端传入空字符串(如
{"username":"", "password":""})时,LoginDTO的username和password会被反序列化为空字符串(而非null),导致AuthManager中的null判断条件不触发。 - 空字符串会进入后续
loadUser流程,如果detailsService.loadUserByUsername对空字符串返回了合法的UserDetails对象(比如默认用户),认证会直接“成功”,不会抛出异常;若该方法抛出异常,也可能被Spring Security全局异常处理机制捕获,导致控制台无输出。
验证方式
在AuthManager的authenticate方法中添加日志,打印凭证实际值:
logger.info("principal value: '{}', credentials value: '{}'", authentication.getPrincipal(), authentication.getCredentials());
如果输出为principal value: '', credentials value: '',即可确认是空字符串导致的问题。
解决方案
1. 修改AuthManager校验逻辑
同时校验null和空字符串(含全空格情况):
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String principal = authentication.getPrincipal() != null ? authentication.getPrincipal().toString().trim() : null; String credentials = authentication.getCredentials() != null ? authentication.getCredentials().toString().trim() : null; if (principal == null || principal.isEmpty() || credentials == null || credentials.isEmpty()) { throw new BadCredentialsException("Credentials are wrong"); } UserDetails user = loadUser(authentication); return new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities()); }
2. 提前在请求参数层面校验
在LoginDTO中添加参数校验注解,提前拦截空凭证请求:
import jakarta.validation.constraints.NotBlank; public class LoginDTO { @NotBlank(message = "Username cannot be empty or whitespace") private String username; @NotBlank(message = "Password cannot be empty or whitespace") private String password; // getter and setter }
并在UsernamePasswordJsonFilter中触发校验:
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { LoginDTO authenticationRequest = objectMapper.readValue(request.getInputStream(), LoginDTO.class); // 手动触发参数校验 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Set<ConstraintViolation<LoginDTO>> violations = validator.validate(authenticationRequest); if (!violations.isEmpty()) { throw new BadCredentialsException(violations.iterator().next().getMessage()); } Authentication auth = new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()); logger.info("UsernamePasswordJsonFilter"); return getAuthenticationManager().authenticate(auth); } catch (IOException e) { throw new RuntimeException(e); } }
内容的提问来源于stack exchange,提问作者huga721
相关产品推荐
相关产品推荐

