我已经尝试了一整天,让我的自定义身份验证失败处理程序与 Spring 3.1.3 一起使用。
我认为它配置正确
<http use-expressions="true" disable-url-rewriting="true">
<intercept-url pattern="/rest/login" access="permitAll" />
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<intercept-url pattern="/index.html" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<form-login username-parameter="user" password-parameter="pass" login-page="/rest/login"
authentication-failure-handler-ref="authenticationFailureHandler" />
</http>
<beans:bean id="authenticationFailureHandler" class="LoginFailureHandler" />
我的实现是这样的
public class LoginFailureHandler implements AuthenticationFailureHandler {
private static final Logger log = LoggerFactory.getLogger(LoginFailureHandler.class);
public LoginFailureHandler() {
log.debug("I am");
}
@Autowired
private ObjectMapper customObjectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
log.debug("invalid login");
User user = new User();
user.setUsername("invalid");
try (OutputStream out = response.getOutputStream()) {
customObjectMapper.writeValue(out, user);
}
}
}
在控制台中我看到
2013-04-11 14:52:29,478 DEBUG LoginFailureHandler - I am
所以它被加载了。
使用错误的用户名或密码,当抛出 BadCredentialsException 时,我看不到无效登录。
永远不会调用方法 onAuthenticationFailure。
相反,该服务一次又一次地将浏览器重定向到/rest/login ...
编辑
2013-04-11 15:47:26,411 DEBUG de.pentos.spring.LoginController - Incomming login chuck.norris, norris
2013-04-11 15:47:26,412 DEBUG o.s.s.a.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2013-04-11 15:47:26,415 DEBUG o.s.s.a.d.DaoAuthenticationProvider - Authentication failed: password does not match stored value
2013-04-11 15:47:26,416 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,419 DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,419 DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public de.pentos.spring.User de.pentos.spring.LoginController.login(de.pentos.spring.User)]: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-04-11 15:47:26,426 DEBUG o.s.web.servlet.DispatcherServlet - Could not complete request
org.springframework.security.authentication.BadCredentialsException: Bad credentials
这发生在 Debug模式下
我的错误在哪里?
最佳答案
从您附加的日志来看,我认为您在实现登录过程时犯了一个错误。我不能绝对肯定,但我猜你调用 ProviderManager.authenticate()
在您的 LoginController
.该方法抛出 BadCredentialsException
这会导致 Spring MVC 的异常处理机制启动,这当然不知道 AuthenticationFailureHandler
为 Spring Security 配置。
在登录 Controller 中,您通常应该使用 action="j_spring_security_check" method="post"
提供一个简单的登录表单。 .当用户提交该表单时,配置的安全过滤器(即 UsernamePasswordAuthenticationFilter
)会拦截该请求并处理身份验证。您不必自己在 Controller 方法中实现该逻辑。
回复您的评论:
你确实使用 ProviderManager
(它是 Autowiring 的 AuthenticationManager
接口(interface)的实现)。您犯的错误是您尝试重写已在身份验证过滤器中实现和彻底测试的逻辑。这本身就是不好的,但即使这样做也是错误的。你从一个复杂的逻辑中只选择了几行,除此之外你忘记了,例如调用 session 策略(以防止 session 固定攻击,并处理并发 session )。原始实现调用 AuthenticationFailureHandler
同样,您在方法中也忘记了这一点,这正是您最初的问题所涉及的问题的原因。
因此,您最终会得到一个未经测试的脆弱解决方案,而不是与框架很好地集成以利用其健壮性和全部容量。正如我所说,您在答案中发布的配置是一个明确的改进,因为它使用框架提供的过滤器进行身份验证。保留该配置并删除 LoginController.login()
, 它不会被发送到 /rest/login
的请求调用.
一个更基本的问题是,如果您实现 RESTful 服务,使用 session 和基于表单的登录机制是否真的是一个很好的解决方案。 (在基于表单的登录中,我的意思是客户端以任何格式发送其凭据一次,然后在后续请求中通过有状态 session 进行身份验证。)使用 REST 服务更普遍的是保持一切无状态,并重新验证每个新请求通过 http header 携带的信息。
关于Spring自定义AuthenticationFailureHandler,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15949665/