Spring Security Web 5.1.2 源码解析 -- SimpleUrlAuthenticationFailureHandler

概述

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;
    }
}

应用位置

  1. FormLoginConfigurer基类AbstractAuthenticationFilterConfigurer
    // 表单认证安全配置阶段指定 failureHandler
    // 缺省情况下,这里 authenticationFailureUrl 是 /login?error
    public final T failureUrl(String authenticationFailureUrl) {
        T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(
                authenticationFailureUrl));
        this.failureUrl = authenticationFailureUrl;
        return result;
    }
  1. UsernamePasswordAuthenticationFilter基类AbstractAuthenticationProcessingFilter
// 在 UsernamePasswordAuthenticationFilter 对象创建时的初始值,不过一般
// 该初始值都会被安全配置中的 failureHandler 覆盖
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
  1. SessionManagementFilter
    SessionManagementFilter在针对认证成功时如果执行有关session策略逻辑失败,也会认为是认证失败,从而需要一个AuthenticationFailureHandler
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

参考文章

Spring Security Web 5.1.2 源码解析 – 框架缺省使用的页面重定向策略

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/andy_zhang2007/article/details/85229465
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞