zoukankan      html  css  js  c++  java
  • Shiro切入Spring的方式

    在springMVC中要使用shiro,一般都遵循下面的配置:

    applicationContext-shiro.xml

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 
        <property name="securityManager" ref="securityManager" />  
    .....
    
    </bean>

    web.xml

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    
    <!-- Spring MVC Servlet -->
    <servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/spring/springmvc-common.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    看到这样的配置,难免有些疑问:
    1. 这三者是怎样融合到一起的
    2. 为什么那个bean的id和filter的name保持一样
    3. 为什么配置的bean是一个factoryBean而不是普通的bean

    带着这样的疑问,跟踪下启动过程。正向跟踪,即在refresh方法中开展,锁定getBean方法,通过name来追踪shiroFilter,发现关系比较乱,而且费时。于是锁定ShiroFilterFactoryBean的getObject方法,反向追踪,获得了下面的序列图:

    image

    重点就是:

    配置的监听器启动容器的初始化,完成上面ShiroFilterFactoryBean的创建和维护

    servlet触发过滤器Filter的init方法,在initDelegate方法中会getBean(),这个getBean最终会转移到ShiroFilterFactoryBean的getObject方法

    上面的问题第二个看源码很容易解决。

    在调用DelegatingFilterProxy初始化方法时:

    @Override
    protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                // If no target bean name specified, use filter name.
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                // Fetch Spring root application context and initialize the delegate early,
                // if possible. If the root application context will be started after this
                // filter proxy, we'll have to resort to lazy initialization.
                WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac);
                }
            }
        }
    }
    
    
    protected final String getFilterName() {
        return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
    }
    
    
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

    获取的这个名字将用来追寻之前spring维护的那个bean,并且还要通过它来拿到一个Filter实例。

    那么为什么配置的那个bean是一个factoryBean而不是一个普通的bean?

    首先shiro是基于过滤器来实现的,配置一个filter是必要的。

    但是和spring在一起使用,就要让spirng来管理一些filter依赖的bean,例如安全管理器,还有自己实现的认证和鉴权服务之类。如果shiro直接写一个过滤器,那spring的容器早就启动完了,你这些东西怎么注入。

    所以只有提前初始化那些过滤器需要的东西,让他们依附于某个特定类,通过约定来在过滤器的初始化中获取(这个就是前面提到的相同的name)。

    那么这个bean可以是普通的bean吗?答案是不可以,毕竟shiro要的这个bean得是一个filter。普通的bean只能获取到它本身的实例,要获取filter那么它必须实现Filter接口。但是创建filter的方法你掌控不了了,spring会通过反射来创建对象,怎么创建是它说了算,自定义的创建是不可能了。

    这时候你就只能使用FactoyBean了。

    这个FactoryBean当然得交给shiro实现。不过filter的创建方式自由了,我们可以通过getBean来获取这个filter,另一边的filter配置还有必要吗?

    当然必要。因为这个filter怎么创建不重要,切入web的生命周期才重要,这个配置是一个规范。所以spring引入了这么一个类: DelegatingFilterProxy。就是一个代理类,自定义的实现转移到factoryBean了,这里就是公共类,主要操作是在容器中追踪并获取之前的factoryBean,并在初始化方法中获取需要的filter。

    Spring容器的启动依靠监听器,而filter是在监听器之后,依靠servlet规范,在listener,filter和servlet三者之间寻找契机,并形成一个共荣圈,Spring把这种关系处理得非常妙!

  • 相关阅读:
    Spring Boot 学习随记
    Prometheus 普罗米修斯监控
    安装VC++6.0步骤及心得
    NFS 系统搭建
    Centos 搭建邮箱系统
    搭建 RTMP 服务器
    阿里云 DTS 实践
    ELK 搭建
    Prometheus 和 Grafana 安装部署
    Centos7 Nagios 搭建
  • 原文地址:https://www.cnblogs.com/lucare/p/8679131.html
Copyright © 2011-2022 走看看