zoukankan      html  css  js  c++  java
  • shiro之深度解析FormAuthenticationFilter

      shiro是我们在项目经常使用到的权限管理框架,本文我们就重点来分析FormAuthenticationFilter的验证过程。

    FormAuthenticationFilter

    1.继承结构

      我们首先来看下FormAuthenticationFilter的继承结构,这样更加便于我们去分析

    在这里插入图片描述

    2.父类的作用

      从上面的继承结构图里我们发现FormAuthenticationFilter的继承结构还是蛮负责的,我们先一个个来介绍下他们的作用。然后具体分析

    作用
    AbstractFilter 加载FilterConfig的相关信息
    NameableFilter 定义每一个filter的名字
    OncePerRequestFilter 保证客户端请求后该filter的doFilter只会执行一次
    AdviceFilter 主要是对doFilterInternal做了更细致的处理
    PathMatchingFilter 主要是对preHandle做进一步细化控制
    AccessControlFilter 对onPreHandle方法做了进一步细化
    AuthenticationFilter
    AuthenticatingFilter
    用来做认证的Filter
    FormAuthenticationFiltershiro 用来具体的实现登录的Filter

    3.具体分析

      接下具体看下每个Filter中主要的方法的作用及实现。

    3.1 NameableFilter

      NameableFilter有一个name属性,定义每一个filter的名字.在FilterChainManager 中会调用配置文件中的配置属性名字来为每一个filter命名以及为默认的filter命名,如authc.

    在这里插入图片描述

    3.2 OncePerRequestFilter

      OncePerRequestFilter保证客户端请求后该filter的doFilter只会执行一次.当满足拦截条件的请求到来的时候执行的就是本方法中的OncePerRequestFilter 中的doFilter方法:

        public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
            if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
                log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
                filterChain.doFilter(request, response);
            } else //noinspection deprecation
                if (/* added in 1.2: */ !isEnabled(request, response) ||
                    /* retain backwards compatibility: */ shouldNotFilter(request) ) {
                log.debug("Filter '{}' is not enabled for the current request.  Proceeding without invoking this filter.",
                        getName());
                filterChain.doFilter(request, response);
            } else {
                // Do invoke this filter...
                log.trace("Filter '{}' not yet executed.  Executing now.", getName());
                request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
    
                try {
                	// 核心代码,保证这个方法只会执行一次
                    doFilterInternal(request, response, filterChain);
                } finally {
                    // Once the request has finished, we're done and we don't
                    // need to mark as 'already filtered' any more.
                    request.removeAttribute(alreadyFilteredAttributeName);
                }
            }
        }
    

      doFilter的实质内容是在doFilterInternal方法中完成的。所以实质上是保证每一个filter的 doFilterInternal只会被执行一次,例如在配置中配置路径 /user/** = authc,authc.则只会执行authc中的doFilterInternal一次。doFilterInternal非常重要,在shiro整个filter体系中的核心方法及实质入口。另外,shiro是通过在request中设置一个该filter特定的属性值来保证该filter只会执行一次的。
      enabled属性决定了是否执行本Filter。

    enabled状态 说明
    true 表示是否启用这个filter,默认是true
    false 跳过这个filter的doFilterInternal方法而去执行filter链中其他filter

    3.3 AdviceFilter

      在OncePerRequestFilter中调用的doFilterInternal方法是个抽象方法,具体的实现是在AdviceFilter中,而且AdviceFilter对于doFilterInternal方法做了更细致的处理。如下:

    在这里插入图片描述

      此处的处理和SpringMVC中的interceptor很类似。

    方法 说明
    preHandle 前置的判断处理,如果返回false则filter链不继续往下执行
    postHandle 在目标方法(即客户端请求的接口)正常(未抛出异常)执行后完成一些操作,默认不做任何操作
    cleanup 会调用afterCompletion方法,不管目标方法是否出现异常都会继续操作。默认也是空

      AdviceFilter总体是对OncePerRequestFilter中的doFilterInternal进一步细化控制

    3.4 PathMatchingFilter

      PathMatchingFilter主要是对preHandle做进一步细化控制,该filter为抽象类,其他路径直接通过:preHandle中,若请求的路径非该filter中配置的拦截路径,则直接返回true进行下一个filter。若包含在此filter路径中,则会在isFilterChainContinued做一些控制,该方法中会调用onPreHandle方法,所以子类可以在onPreHandle中编写filter控制流程代码(返回true或false)

    在这里插入图片描述

    在这里插入图片描述

    3.5 AccessControlFilter

      onPreHandle方法在PathMatchingFilter就简单的返回了true,在AccessControlFilter 中更细化的实现了。

       public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) 
       throws Exception {
           return isAccessAllowed(request, response, mappedValue) 
           || onAccessDenied(request, response, mappedValue);
       }
    

      isAccessAllowed方法和onAccessDenied方法达到控制效果。这两个方法都是抽象方法,由子类去实现。到这一层应该明白。isAccessAllowed和onAccessDenied方法会影响到onPreHandle方法,而onPreHandle方法会影响到preHandle方法,而preHandle方法会达到控制filter链是否执行下去的效果。所以如果正在执行的filter中isAccessAllowed和onAccessDenied都返回false,则整个filter控制链都将结束,不会到达目标方法(客户端请求的接口),而是直接跳转到某个页面(由filter定义的,将会在authc中看到)

    3.6 AuthenticatingFilter

      AuthenticationFilter和AuthenticatingFilter认证的filter,在抽象类中AuthenticatingFilter实现了isAccessAllowed方法,该方法是用来判断用户是否已登录,若未登录再判断是否请求的是登录地址,是登录地址则放行,否则返回false终止filter链。

    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        Subject subject = getSubject(request, response);
        return subject.isAuthenticated();
    }
    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return super.isAccessAllowed(request, response, mappedValue)
         ||(!isLoginRequest(request, response) 
         && isPermissive(mappedValue));
    }
    

      提供了executeLogin方法实现用户登录的,还定义了onLoginSuccess和onLoginFailure方法,在登录成功或者失败时做一些操作。登录将在下面详细说明

    在这里插入图片描述

    3.7 FormAuthenticationFiltershiro

      FormAuthenticationFiltershiro提供的登录的filter,如果用户未登录,即AuthenticatingFilter中的isAccessAllowed判断了用户未登录,则会调用onAccessDenied方法做用户登录操作。若用户请求的不是登录地址,则跳转到登录地址,并且返回false直接终止filter链。若用户请求的是登录地址,若果是post请求则进行登录操作,由AuthenticatingFilter中提供的executeLogin方法执行。否则直接通过继续执行filter链,并最终跳转到登录页面(因为用户请求的就是登录地址,若不是登录地址也会重定向到登录地址)

    在这里插入图片描述

      在来看executeLogin方法,

    在这里插入图片描述

    onLoginSuccess

    在这里插入图片描述

    onLoginFailure

    在这里插入图片描述

    在这里插入图片描述

      若登录成功返回false(FormAuthenticationFiltershiro的onLoginSuccess默认false),则表示终止filter链,直接重定向到成功页面,甚至不到达目标方法直接返回了。若登录失败,直接返回true(onLoginFailure返回false),继续执行filter链并最终跳转到登录页面,该方法还会设置一些登录失败提示 shiroLoginFailure,在目标方法中可以根据这个错误提示制定客户端更加友好的错误提示。

  • 相关阅读:
    GCC编译器原理(三)------编译原理三:编译过程(3)---编译之汇编以及静态链接【2】
    xmind转为markdown
    XMind破解
    读取字符串中的数字
    "Cannot declare member function ...to have static linkage"错误
    error: cannot bind non-const lvalue reference of type
    (C++ Error: Incompatible types in assignment of ‘char*’ to ‘char [2])
    C++中的map
    17 jQuery阶段_操作样式_操作属性_动画_节点操作
    第一部分:网络多任务_day01_网络编程基础
  • 原文地址:https://www.cnblogs.com/dengpengbo/p/10794346.html
Copyright © 2011-2022 走看看