shirofilter
Posted but-you
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shirofilter相关的知识,希望对你有一定的参考价值。
alt+7
OncePerRequestFilter
1 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 2 throws ServletException, IOException { 3 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 4 if ( request.getAttribute(alreadyFilteredAttributeName) != null ) { 5 log.trace("Filter ‘{}‘ already executed. Proceeding without invoking this filter.", getName()); 6 filterChain.doFilter(request, response); 7 } else //noinspection deprecation 8 if (/* added in 1.2: */ !isEnabled(request, response) || 9 /* retain backwards compatibility: */ shouldNotFilter(request) ) { 10 log.debug("Filter ‘{}‘ is not enabled for the current request. Proceeding without invoking this filter.", 11 getName()); 12 filterChain.doFilter(request, response); 13 } else { 14 // Do invoke this filter... 15 log.trace("Filter ‘{}‘ not yet executed. Executing now.", getName()); 16 request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); 17 18 try { 19 doFilterInternal(request, response, filterChain); //保证客户端请求后该filter的doFilter只会执行一次。 20 } finally { 21 // Once the request has finished, we‘re done and we don‘t 22 // need to mark as ‘already filtered‘ any more. 23 request.removeAttribute(alreadyFilteredAttributeName); 24 } 25 } 26 }
可以看出doFilter的实质内容是在doFilterInternal方法中完成的。
所以实质上是保证每一个filter的 doFilterInternal只会被执行一次,
例如在配置中 配置路径 /user/** = authc,authc.则只会执行authc中的doFilterInternal一次。
doFilterInternal非常重要,在shiro整个filter体系中的核心方法及实质入口。
另外,shiro是通过在request中设置一个该filter特定的属性值来保证该filter只会执行一次的。 其次可以看到有一个enabled属性,表示是否启用这个filter,默认是true,可以设置成false,
则会跳过这个filter的doFilterInternal方法而去执行filter链中其他filter。(filterChain.doFilter(request, response);放行)
AdviceFilter中主要是对doFilterInternal做了更细致的切分
1 public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) 2 throws ServletException, IOException { 3 4 Exception exception = null; 5 6 try { 7 8 boolean continueChain = preHandle(request, response); 9 if (log.isTraceEnabled()) { 10 log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); 11 } 12 13 if (continueChain) { 14 executeChain(request, response, chain); //doFilterInternal(request, response, filterChain); 15 } 16 17 postHandle(request, response); //进入我们自己的方法逻辑,有点像 interceptor拦截器 18 if (log.isTraceEnabled()) { 19 log.trace("Successfully invoked postHandle method"); 20 } 21 22 } catch (Exception e) { 23 exception = e; 24 } finally { 25 cleanup(request, response, exception); 26 } 27 }
PathMatchingFilter主要是对preHandle做进一步细化控制,该filter为抽象类,其他路径直接通过:preHandle中,
若请求的路径非该filter中配置的拦截路径,则直接返回true进行下一个filter。
若包含在此filter路径中,则会在isFilterChainContinued做一些控制,
该方法中会调用onPreHandle方法,
所以子类可以在onPreHandle中编写filter控制流程代码(返回true或false)。
1 protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 2 3 if (this.appliedPaths == null || this.appliedPaths.isEmpty()) { 4 if (log.isTraceEnabled()) { 5 log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately."); 6 } 7 return true; 8 } 9 10 for (String path : this.appliedPaths.keySet()) { 11 // If the path does match, then pass on to the subclass implementation for specific checks 12 //(first match ‘wins‘): 13 if (pathsMatch(path, request)) { 14 log.trace("Current requestURI matches pattern ‘{}‘. Determining filter chain execution...", path); 15 Object config = this.appliedPaths.get(path); 16 return isFilterChainContinued(request, response, path, config); 17 } 18 } 19 20 //no path matched, allow the request to go through: 21 return true; 22 } 23 24 private boolean isFilterChainContinued(ServletRequest request, ServletResponse response, 25 String path, Object pathConfig) throws Exception { 26 27 if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2 点击在OncePerRequestFilter里头 默认true 28 if (log.isTraceEnabled()) { 29 log.trace("Filter ‘{}‘ is enabled for the current request under path ‘{}‘ with config [{}]. " + 30 "Delegating to subclass implementation for ‘onPreHandle‘ check.", 31 new Object[]{getName(), path, pathConfig}); 32 } 33 //The filter is enabled for this specific request, so delegate to subclass implementations 34 //so they can decide if the request should continue through the chain or not: 35 return onPreHandle(request, response, pathConfig); 36 } 37 38 if (log.isTraceEnabled()) { 39 log.trace("Filter ‘{}‘ is disabled for the current request under path ‘{}‘ with config [{}]. " + 40 "The next element in the FilterChain will be called immediately.", 41 new Object[]{getName(), path, pathConfig}); 42 } 43 //This filter is disabled for this specific request, 44 //return ‘true‘ immediately to indicate that the filter will not process the request 45 //and let the request/response to continue through the filter chain: 46 return true; 47 } 48 49 protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { 50 return true; 51 }
AccessControlFilter
AccessControlFilter中的对onPreHandle方法做了进一步细化,isAccessAllowed方法和onAccessDenied方法达到控制效果
。这两个方法都是抽象方法,由子类去实现。到这一层应该明白。isAccessAllowed和onAccessDenied方法会影响到onPreHandle方法,而onPreHandle方法会影响到preHandle方法,
而preHandle方法会达到控制filter链是否执行下去的效果。所以如果正在执行的filter中isAccessAllowed和onAccessDenied都返回false,
则整个filter控制链都将结束,不会到达目标方法(客户端请求的接口),而是直接跳转到某个页面(由filter定义的,将会在authc中看到)。
⑥AuthenticationFilter和AuthenticatingFilter认证的filter,在抽象类中AuthenticatingFilter实现了isAccessAllowed方法,该方法是用来判断用户是否已登录,若未登录再判断是否请求的是登录地址,是登录地址则放行,否则返回false终止filter链。
另外可以看到提供了executeLogin方法实现用户登录的,还定义了onLoginSuccess和onLoginFailure方法,在登录成功或者失败时做一些操作。登录将在下面详细说明。
⑦FormAuthenticationFiltershiro提供的登录的filter,如果用户未登录,即AuthenticatingFilter中的isAccessAllowed判断了用户未登录,则会调用onAccessDenied方法做用户登录操作。
若用户请求的不是登录地址,则跳转到登录地址,并且返回false直接终止filter链。
若用户请求的是登录地址,如果是post请求则进行登录操作,由AuthenticatingFilter中提供的executeLogin方法执行。
否则直接通过继续执行filter链,并最终跳转到登录页面(因为用户请求的就是登录地址,若不是登录地址也会重定向到登录地址).
AuthenticatingFilter中的executeLogin
若登录成功返回false(FormAuthenticationFiltershiro的onLoginSuccess默认false),则表示终止filter链,直接重定向到成功页面,甚至不到达目标方法直接返回了。
若登录失败,直接返回true(onLoginFailure返回false),继续执行filter链并最终跳转到登录页面,该方法还会设置一些登录失败提示 shiroLoginFailure,
在目标方法中可以根据这个错误提示制定客户端更加友好的错误提示。
二 自定义filter
一般自定义filter可以继承三种:
①OncePerRequestFilter只需实现doFilterInternal方法即可,在这里面实现filter的功能。切记在该方法中最后调用filterChain.doFilter(request, response),允许filter链继续执行下去。可以在这个自定义filter中覆盖isEnable达到控制该filter是否需要被执行(实质是doFilterInternal方法)以达到动态控制的效果,一般不建议直接继承这个类;
②AdviceFilter 中提供三个方法preHandle postHandle afterCompletion:若需要在目标方法执行前后都做一些判断的话应该继承这个类覆盖preHandle 和postHandle 。
③PathMatchingFilter中preHandle实质会判断onPreHandle来决定是否继续往下执行。所以只需覆盖onPreHandle方法即可。
④AccessControlFilter:最常用的,该filter中onPreHandle调用isAccessAllowed和onAccessDenied决定是否继续执行。一般继承该filter,isAccessAllowed决定是否继续执行。onAccessDenied做后续的操作,如重定向到另外一个地址、添加一些信息到request域等等。
④若要自定义登录filter,一般是由于前端传过来的需求所定义的token与shiro默认提供token的不符,可以继承AuthenticatingFilter ,在这里面实现createToken来创建自定义token。另外需要自定义凭证匹配器credentialsMatcher。重写public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)即可。realm也需要自定义以返回自定义的token。
三shiro登录
由前面filter链可以看出登录已经很清晰了。shiro提供的FormAuthenticationFilter认证过滤器,继承了AuthenticatingFilter ,若已登录则isAccessAllowed直接通过,否则在 onAccessDenied中判断是否是登录请求,若是请求登录页面,直接通过,若是post提交登录信息 则会进行登录操作。否则直接跳转到登录页面。登录是由shiro的securityManager完成的,securityManager从Realm获取用户的真实身份,从FormAuthenticationFilter的createToken获取用户提交的token,credentialsMatcher完成是否匹配成功操作。
原作者:https://www.cnblogs.com/yoohot/p/6085830.html
以上是关于shirofilter的主要内容,如果未能解决你的问题,请参考以下文章
Shiro系列之Shiro+Spring MVC整合(Integration)