Spring Security自定义过滤器验证失败后如何重定向至登录页?
看起来你遇到的核心问题是:自定义AD验证失败时,系统没有按照配置跳转到/login?error,而是直接走到了403或其他错误页面。这是因为你的过滤器在验证失败时没有正确抛出Spring Security可识别的认证异常,导致后续的异常处理流程没有触发。
问题根源分析
你当前的逻辑是AD验证失败后,构造了一个空的UsernamePasswordAuthenticationToken交给AuthenticationManager去认证,这会触发内部的凭证错误异常,但这个流程没有被Spring Security的ExceptionTranslationFilter正确捕获处理——因为你提前拦截了登录请求,却没有正确传递认证失败的信号。
解决方案步骤
1. 直接抛出认证异常,替代空Token认证
当AD验证失败时,不要构造空Token,直接抛出BadCredentialsException(或者自定义的AuthenticationException子类),这样Spring Security的异常处理链会自动捕获这个异常,并跳转到你配置的authentication-failure-url。
修改你的attemptAuthentication方法:
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { String requestBody; try { requestBody = IOUtils.toString(request.getReader()); ArrayList<String> credenciales = new ArrayList<String>(); for (String val : requestBody.split("&")) { credenciales.add(val.split("=")[1]); } try { org.tempuri.ADWS service = new org.tempuri.ADWS(); org.tempuri.IADWS port = service.getBasicHttpBindingIADWS(); boolean valido = port.login(credenciales.get(0), credenciales.get(1)); if (!valido) { LOG.error("El usuario " + credenciales.get(0) + " no existe en AD"); // 直接抛出认证失败异常 throw new BadCredentialsException("Usuario o contraseña inválidos en AD"); } } catch (Exception ex) { LOG.error(ERROR_MESSAGE, ex); throw new InternalAuthenticationServiceException(ERROR_MESSAGE, ex); } // AD验证通过,正常构造Token UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credenciales.get(0), credenciales.get(1)); setDetails(request, token); return this.getAuthenticationManager().authenticate(token); } catch (IOException e) { LOG.error(ERROR_MESSAGE, e); throw new InternalAuthenticationServiceException(ERROR_MESSAGE, e); } }
2. 确保过滤器继承的异常处理逻辑生效
你的过滤器继承了UsernamePasswordAuthenticationFilter,这个类本身已经集成了认证失败的处理逻辑,但需要确保你没有覆盖unsuccessfulAuthentication方法——如果覆盖了,需要手动调用getFailureHandler().onAuthenticationFailure(request, response, exception)来触发跳转。
如果你的过滤器没有覆盖这个方法,那上面抛出异常后,父类的unsuccessfulAuthentication会自动调用配置的AuthenticationFailureHandler,跳转到你在<form-login>里配置的authentication-failure-url="/login?error"。
3. 检查XML配置的过滤器顺序
你已经配置了<custom-filter ref="authenticationFilter" before="FORM_LOGIN_FILTER" />,这个顺序是对的,确保你的过滤器在默认的表单登录过滤器之前处理请求,不会被默认逻辑拦截。
额外优化建议
- 不要用
IOUtils手动解析请求体,UsernamePasswordAuthenticationFilter已经有obtainUsername和obtainPassword方法,你可以重写这两个方法来获取请求参数,更符合Spring Security的规范。 - 自定义异常类可以让错误信息更清晰,但
BadCredentialsException是Spring Security的标准异常,足够满足你的需求。
这样修改后,当AD验证失败时,会直接抛出认证异常,Spring Security会自动跳转到/login?error页面,和你配置的表单登录失败逻辑保持一致。
内容的提问来源于stack exchange,提问作者Martín Cabo

