您的位置:首页 > 其它

shiro之深度解析FormAuthenticationFilter

2019-04-30 09:53 87 查看

  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==,在目标方法中可以根据这个错误提示制定客户端更加友好的错误提示。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: