设为首页 收藏本站
查看: 1123|回复: 0

[经验分享] Apache Shiro学习笔记(六)Shiro Filter介绍

[复制链接]

尚未签到

发表于 2018-11-19 12:08:51 | 显示全部楼层 |阅读模式
鲁春利的工作笔记,好记性不如烂笔头
  

  ShiroFilter
DSC0000.jpg

  

  ShiroFilter,它是在整个 Shiro Web 应用中请求的门户,所有的请求都会被 ShiroFilter 拦截并进行相应的链式处理。ShiroFilter 往上有五层,最上层是 Filter(即 javax.servlet.Filter),它是 Servlet 规范中的 Filter 接口。

  

  1、AbstractFilter
  Shiro通过抽象类对Servlet的Filter接口进行了封装,并通过继承ServletContextSupport对ServletContext也进行了封装。
package org.apache.shiro.web.servlet;
// ServletContextSupport对ServletContext进行了封装
public abstract class AbstractFilter extends ServletContextSupport implements Filter {
    protected FilterConfig filterConfig;
    public FilterConfig getFilterConfig() {
        return filterConfig;
    }
    // 初始化 FilterConfig 与 ServletContext
    public void setFilterConfig(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
        // 调用ServletContextSupport的方法来封装ServletContext
        setServletContext(filterConfig.getServletContext());
    }
    // 从 FilterConfig 中获取初始参数
    protected String getInitParam(String paramName) {
        FilterConfig config = getFilterConfig();
        if (config != null) {
            return StringUtils.clean(config.getInitParameter(paramName));
        }
        return null;
    }
    public final void init(FilterConfig filterConfig) throws ServletException {
        // 初始化 FilterConfig
        setFilterConfig(filterConfig);
        try {
            // 在子类中实现该模板方法
            onFilterConfigSet();
        } catch (Exception e) {
            // 异常信息
        }
    }
}  Shiro对ServletContext的封装
  Shiro 为了封装 ServletContext 的而提供的一个类ServletContextSupport
package org.apache.shiro.web.servlet;
//
public class ServletContextSupport {
    private ServletContext servletContext = null;
    public ServletContext getServletContext() {
        return servletContext;
    }
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }
    // 其他代码略
}  

  2、NameableFilter
  提供了Filter Name的获取方式。
package org.apache.shiro.web.servlet;
public abstract class NameableFilter extends AbstractFilter implements Nameable {
    /**
     * The name of this filter, unique within an application.
     */
    private String name;
    // 若成员变量 name 为空,则从 FilterConfig 中获取 Filter Name
    protected String getName() {
        if (this.name == null) {
            FilterConfig config = getFilterConfig();
            if (config != null) {
                this.name = config.getFilterName();
            }
        }
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
}  每个 Filter 必须有一个名字,可通过 setName 方法设置的,如果不设置就取该 Filter 默认的名字,也就是在 web.xml 中配置的 filter-name 了。

  

  3、OncePerRequestFilter
  NameableFilter是为了让每个 Filter 有一个名字,而且这个名字必须是唯一的。此外,在 shiro.ini 的 [urls] 片段的配置要求满足一定规则,例如:

[urls]
/foo = ssl, authc  等号左边的是 URL,右边的是 Filter Chian,一个或多个 Filter,每个 Filter 用逗号进行分隔。

  对于 /foo 这个 URL 而言,可先后通过 ssl 与 authc 这两个 Filter。如果我们同时配置了两个 ssl,这个 URL 会被 ssl 拦截两次吗?答案是否定的,因为 Shiro 为我们提供了一个“一次性Filter”的原则,也就是保证了每个请求只能被同一个 Filter 拦截一次,而且仅此一次。

package org.apache.shiro.web.servlet;
public abstract class OncePerRequestFilter extends NameableFilter {
     /**
     * 已执行过的过滤器("already filtered")附加的后缀名
     *
     * @see #getAlreadyFilteredAttributeName
     */
    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
    /**
     * 是否开启过滤功能
     *
     * @see #isEnabled()
     */
    private boolean enabled = true; //most filters wish to execute when configured, so default to true
    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // 获取 Filter 已过滤的属性名
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
        // 判断是否已过滤
        if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
            // 若已过滤,则进入 FilterChain 中下一个 Filter
            filterChain.doFilter(request, response);
        // 若未过滤,则判断是否未开启过滤功能(其中 shouldNotFilter 方法将被废弃
        } else if (!isEnabled(request, response) || shouldNotFilter(request) ) {
            // 若未开启,则进入 FilterChain 中下一个 Filter
            filterChain.doFilter(request, response);
        } else {
            // Do invoke this filter...(执行过滤)
            // 将已过滤属性设置为 true(只要保证 Request 中有这个属性即可)
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
            try {
                // 在子类中执行具体的过滤操作
                doFilterInternal(request, response, filterChain);
            } finally {
                // 当前 Filter 执行结束需移除 Request 中的已过滤属性
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }
    protected String getAlreadyFilteredAttributeName() {
        String name = getName();
        if (name == null) {
            name = getClass().getName();
        }
        return name + ALREADY_FILTERED_SUFFIX;
    }
    // 抽象方法,由子类实现
    protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException;
}  如何确保每个请求只会被同一个 Filter 拦截一次呢?Shiro 提供了一个超简单的解决方案:在 Requet 中放置一个后缀为 .FILTERED 的属性,在执行具体拦截操作(即 doFilterInternal 方法)之前放入该属性,执行完毕后移除该属性。
  在 Shiro 的 Filter Chian 配置中,如果我们想禁用某个 Filter,如何实现呢?OncePerRequestFilter 也为我们提供了一个 enabled 的属性,方便我们可以在 shiro.ini 中随时禁用某个 Filter,例如:
[main]
ssl.enabled = false
[urls]
/foo = ssl, authc  这样一来 ssl 这个 Filter 就被我们给禁用了,以后想开启 ssl 的话,完全不需要在 urls配置中一个个手工来添加,只需把 ssl.enabled 设置为 true,或注释掉该行,或直接删除该行即可。

  可见,OncePerRequestFilter 给我们提供了一个模板方法doFilterInternal,在其子类中我们需要实现该方法的具体细节,那么谁来实现呢?
  

  3.1、AbstractShiroFilter
package org.apache.shiro.web.servlet;
/**
* 确保可通过 SecurityUtils 获取 SecurityManager,并执行过滤器操作
*/
public abstract class AbstractShiroFilter extends OncePerRequestFilter {
    // 是否可以通过 SecurityUtils 获取 SecurityManager
    private static final String STATIC_INIT_PARAM_NAME = "staticSecurityManagerEnabled";
    // Reference to the security manager used by this filter
    private WebSecurityManager securityManager;
    // Used to determine which chain should handle an incoming request/response
    private FilterChainResolver filterChainResolver;
    private boolean staticSecurityManagerEnabled;
    protected AbstractShiroFilter() {
        this.staticSecurityManagerEnabled = false;
    }
    public WebSecurityManager getSecurityManager() {
        return securityManager;
    }
    public void setSecurityManager(WebSecurityManager sm) {
        this.securityManager = sm;
    }
    public FilterChainResolver getFilterChainResolver() {
        return filterChainResolver;
    }
    public void setFilterChainResolver(FilterChainResolver filterChainResolver) {
        this.filterChainResolver = filterChainResolver;
    }
    // 在AbstractFilter.ini()中调用子类的实现
    protected final void onFilterConfigSet() throws Exception {
        //added in 1.2 for SHIRO-287:
        // 从 web.xml 中读取 staticSecurityManagerEnabled 参数(默认为 false)
        applyStaticSecurityManagerEnabledConfig();
        // 初始化(在子类中实现)
        init();
        // 确保 SecurityManager 必须存在
        ensureSecurityManager();
        //added in 1.2 for SHIRO-287:
        // 若已开启 static 标志,则将当前的 SecurityManager 放入 SecurityUtils 中,以后可以随时获取
        if (isStaticSecurityManagerEnabled()) {
            SecurityUtils.setSecurityManager(getSecurityManager());
        }
    }
    public void init() throws Exception {
        // 空方法体
    }
    // OncePerRequestFilter.doFilter 时需要执行的方法
    protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
                                    final     FilterChain chain)throws ServletException, IOException {
        Throwable t = null;
        try {
            // 通过ShiroHttpServletRequest对ServletRequest进行了封装
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            // 通过ShiroHttpServletResponse对ServletResponse进行了封装
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
            // 创建 Shiro 的 Subject 对象(WebSubject)
            final Subject subject = createSubject(request, response);
            // 使用异步的方式执行相关操作
            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    // 更新 Session 的最后访问时间
                    updateSessionLastAccessTime(request, response);
                    // 执行 Shiro 的 Filter Chain
                    executeChain(request, response, chain);
                    return null;
                }
            });
        } catch (ExecutionException ex) {
            // 异常处理
        } catch (Throwable throwable) {
            // 异常处理
        }
        if (t != null) {
            // 异常处理
        }
    }
    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        // 获取 Shiro 代理后的 FilterChain 对象,并进行链式处理
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
    }
    protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;
        // 在ShiroFilter的init方法中设置FilterChainResolver
        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            log.debug("No FilterChainResolver configured.  Returning original FilterChain.");
            return origChain;
        }
        // 通过 FilterChainResolver(PathMatchingFilterChainResolver) 获取 ProxiedFilterChain
        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            log.trace("Resolved a configured FilterChain for the current request.");
            chain = resolved;
        } else {
            log.trace("No FilterChain configured for the current request.  Using the default.");
        }
        return chain;
    }
}  

  3.1.1、ShiroFilter
package org.apache.shiro.web.servlet;
public class ShiroFilter extends AbstractShiroFilter {
    @Override
    public void init() throws Exception {
        // 从 ServletContext 中获取 WebEnvironment(该对象已通过 EnvironmentLoader 创建)
        // 实际实现为:通过ServletContext获取到web.xml文件中定义的shiroEnvironmentClass
        WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());
        // IniWebEnvironment.init会生成SecurityManager,在getWebSecurityManager时直接获取到
        setSecurityManager(env.getWebSecurityManager());
        // 通过IniFilterChainResolverFactory.createDefaultInstance获取PathMatchingFilterChainResolver
        FilterChainResolver resolver = env.getFilterChainResolver();
        if (resolver != null) {
            setFilterChainResolver(resolver);
        }
    }
}  在 ShiroFilter 中只用做初始化的行为,就是从 WebEnvironment 中分别获取 WebSecurityManager与FilterChainResolver,其它的事情都由它的父类去实现了。
实际上ShiroFilter 还实现了一些其他的封装,例如:
    通过 ShiroHttpServletRequest 来包装 Request
    通过 ShiroHttpServletResponse 来包装 Response
    通过 Session 来代理 HttpSession
    提供 FilterChain 的代理机制
    使用 ThreadContext 来保证线程安全
  





运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-636988-1-1.html 上篇帖子: Apache Shiro学习笔记(六)Servlet3.0 Filter介绍 下篇帖子: LAMP_apache安装_2
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表