我已经尝试了一整天,让我的自定义身份验证失败处理程序与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时,我看不到无效登录。
永远不会调用Method 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
这在调试模式中发生
我的错误在哪里?
答案 0 :(得分:3)
根据您附加的日志判断我认为您在实施登录过程时犯了一个错误。我不能完全确定,但我猜您在ProviderManager.authenticate()
中致电LoginController
。该方法抛出一个BadCredentialsException
导致Spring MVC的异常处理机制启动,当然它不知道为Spring Security配置的AuthenticationFailureHandler
。
通过登录控制器,您通常只需提供一个包含action="j_spring_security_check" method="post"
的简单登录表单。当用户提交该表单时,配置的安全筛选器(即UsernamePasswordAuthenticationFilter
)将拦截该请求并处理身份验证。您不必在控制器方法中自己实现该逻辑。
您确实使用ProviderManager
(它是自动连接的AuthenticationManager
接口的实现)。您犯的错误是您尝试重写已经实现的逻辑并在auth过滤器中进行彻底测试。这本身就很糟糕,但即使这样做也是错误的。您只需从复杂的逻辑中选择几行,除此之外您还会忘记例如调用会话策略(以防止会话固定攻击和处理并发会话)。原始实现调用AuthenticationFailureHandler
同样,你也忘记了你的方法,这就是你原来问题的问题的原因。
因此,您最终会得到一个未经测试的脆弱解决方案,而不是与框架很好地集成,以充分利用其抢夺性和全部容量。正如我所说,您在答案中发布的配置是一个明确的改进,因为它使用框架提供的过滤器进行身份验证。保留该配置并删除LoginController.login()
,发送给/rest/login
的请求无论如何都不会调用它。
一个更基本的问题是,如果您实施RESTful服务,它是否真的是一个使用会话和基于表单的登录机制的好解决方案。 (在基于表单的登录时,我的意思是客户端以任何格式发送其凭据一次,然后通过有状态会话对后续请求进行身份验证。)使用REST服务,保持所有无状态更为普遍,并通过http标头携带的信息重新验证每个新请求。
答案 1 :(得分:1)
这是security-app-context.xml中的顺序问题。
如果我先定义所有的bean,那么所有其余的都可以。 我尝试了很多,所以不要怀疑,它现在看起来与问题
有点不同<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/rest/login" />
</beans:bean>
<beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/rest/login" />
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</beans:bean>
<beans:bean id="authenticationSuccessHandler" class="de.pentos.spring.LoginSuccessHandler" />
<beans:bean id="authenticationFailureHandler" class="de.pentos.spring.LoginFailureHandler" />
<http use-expressions="true" disable-url-rewriting="true" entry-point-ref="authenticationProcessingFilterEntryPoint"
create-session="ifRequired">
<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" />
<custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="chuck.norris" password="cnorris" authorities="ROLE_ADMIN" />
<user name="user" password="user" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
答案 2 :(得分:0)
对我来说不好看。您是否尝试使用IDE的调试模式?
您是否在日志中看到了类似的内容:
Authentication request failed: ...
Updated SecurityContextHolder to contain null Authentication
Delegating to authentication failure handler ...
只有在其中一个身份验证过滤器中完成身份验证时,才会自动调用AuthenticationFailureHandler:通常情况下是UsernamePasswordAuthenticationFilter。
答案 3 :(得分:0)
(查看您的要求),您不需要使用自定义AuthenticationFailureHandler作为Spring的默认SimpleUrlAuthenticationFailureHandler,并且正确实现AuthenticationProvider应该可以实现此目的。
<form-login login-page="/login" login-processing-url="/do/login" authentication- failure-url ="/login?authfailed=true" authentication-success-handler-ref ="customAuthenticationSuccessHandler"/>
如果您已在身份验证提供程序中处理好例外:
示例逻辑:
String loginUsername = (String) authentication.getPrincipal();
if (loginUsername == null)
throw new UsernameNotFoundException("User not found");
String loginPassword = (String) authentication.getCredentials();
User user = getUserByUsername(loginUsername);
UserPassword password = getPassword(user.getId());
if (!password.matches(loginPassword)) {
throw new BadCredentialsException("Invalid password.");
}
如果我们希望抛出的异常反映在客户端界面上,请在JSP上添加以下scriplet,以响应 authentication-failure-url =“/ login?authfailed = true”
<%
Exception error = (Exception) request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
if (error != null)
out.write(error.getMessage());
%>