概述
AuthenticationFailureHandler
接口定义了Spring Security Web
在遇到认证错误时所使用的处理策略。
典型做法一般是将用户重定向到认证页面(比如认证机制是用户名表单认证的情况)让用户再次认证。当然具体实现类可以根据需求实现更复杂的逻辑,比如根据异常做不同的处理等等。举个例子,如果遇到CredentialsExpiredException
异常(AuthenticationException
异常的一种,表示密码过期失效),可以将用户重定向到修改密码页面而不是登录认证页面。
该接口定义如下 :
package org.springframework.security.web.authentication;
public interface AuthenticationFailureHandler {
/** * 认证失败时会调用此方法 * @param request 出现认证失败时所处于的请求. * @param response 对应上面请求的响应对象. * @param exception 携带认证失败原因的认证失败异常对象 * request. */
void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException;
}
在Spring Security Web
框架内部,缺省使用的认证错误处理策略是AuthenticationFailureHandler
的实现类SimpleUrlAuthenticationFailureHandler
。它由配置指定一个defaultFailureUrl
,表示认证失败时缺省使用的重定向地址。一旦认证失败,它的方法onAuthenticationFailure
被调用时,它就会将用户重定向到该地址。如果该属性没有设置,它会向客户端返回一个401
状态码。另外SimpleUrlAuthenticationFailureHandler
还有一个属性useForward
,如果该属性设置为true
,页面跳转将不再是重定向(redirect
)机制,取而代之的是转发(forward
)机制。
源代码解析
具体实现代码如下 :
package org.springframework.security.web.authentication;
public class SimpleUrlAuthenticationFailureHandler implements
AuthenticationFailureHandler {
protected final Log logger = LogFactory.getLog(getClass());
// 认证失败时缺省使用的重定向地址
private String defaultFailureUrl;
// 是否使用 forward, 缺省为 false, 表示使用 redirect
private boolean forwardToDestination = false;
// 是否在需要session的时候允许创建session
private boolean allowSessionCreation = true;
// 页面重定向策略
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public SimpleUrlAuthenticationFailureHandler() {
}
public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
setDefaultFailureUrl(defaultFailureUrl);
}
/** * Performs the redirect or forward to the defaultFailureUrl if set, otherwise * returns a 401 error code. * * If redirecting or forwarding, saveException will be called to cache the * exception for use in the target view. */
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if (defaultFailureUrl == null) {
logger.debug("No failure URL set, sending 401 Unauthorized error");
// 如果 defaultFailureUrl 没有设置,向客户端返回 401 错误 : Unauthorized
response.sendError(HttpStatus.UNAUTHORIZED.value(),
HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
else {
saveException(request, exception);
if (forwardToDestination) {
// 指定了使用 forward 的情况
logger.debug("Forwarding to " + defaultFailureUrl);
request.getRequestDispatcher(defaultFailureUrl)
.forward(request, response);
}
else {
// 指定了使用 redirect 的情况 , 缺省情况
logger.debug("Redirecting to " + defaultFailureUrl);
redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
}
}
}
/** * Caches the AuthenticationException for use in view rendering. * * If forwardToDestination is set to true, request scope will be used, * otherwise it will attempt to store the exception in the session. If there is no * session and allowSessionCreation is true a session will be created. * Otherwise the exception will not be stored. */
protected final void saveException(HttpServletRequest request,
AuthenticationException exception) {
if (forwardToDestination) {
// forward 的情况,保存异常到 request 属性 : SPRING_SECURITY_LAST_EXCEPTION
request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
}
else {
// redirect 的情况 , 保存异常到 session : SPRING_SECURITY_LAST_EXCEPTION
HttpSession session = request.getSession(false);
if (session != null || allowSessionCreation) {
request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
exception);
}
}
}
/** * The URL which will be used as the failure destination. * * @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp". */
public void setDefaultFailureUrl(String defaultFailureUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl),
() -> "'" + defaultFailureUrl + "' is not a valid redirect URL");
this.defaultFailureUrl = defaultFailureUrl;
}
protected boolean isUseForward() {
return forwardToDestination;
}
/** * If set to true, performs a forward to the failure destination URL instead * of a redirect. Defaults to false. */
public void setUseForward(boolean forwardToDestination) {
this.forwardToDestination = forwardToDestination;
}
/** * Allows overriding of the behaviour when redirecting to a target URL. */
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
protected boolean isAllowSessionCreation() {
return allowSessionCreation;
}
public void setAllowSessionCreation(boolean allowSessionCreation) {
this.allowSessionCreation = allowSessionCreation;
}
}
应用位置
FormLoginConfigurer
基类AbstractAuthenticationFilterConfigurer
// 表单认证安全配置阶段指定 failureHandler
// 缺省情况下,这里 authenticationFailureUrl 是 /login?error
public final T failureUrl(String authenticationFailureUrl) {
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(
authenticationFailureUrl));
this.failureUrl = authenticationFailureUrl;
return result;
}
UsernamePasswordAuthenticationFilter
基类AbstractAuthenticationProcessingFilter
// 在 UsernamePasswordAuthenticationFilter 对象创建时的初始值,不过一般
// 该初始值都会被安全配置中的 failureHandler 覆盖
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
SessionManagementFilter
SessionManagementFilter
在针对认证成功时如果执行有关session
策略逻辑失败,也会认为是认证失败,从而需要一个AuthenticationFailureHandler
。
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();