Shiro源码(一)

首先推荐两篇与Shiro相关的源码分析文章:

Spring DelegatingFilterProxy 源码分析

Shiro源码分析之ShiroFilterFactoryBean

本文是对上述第两篇文章的补充:

首先是对第二篇文章中ShiroFilterFactoryBean#createFilterChainManager 分析的代码中引用方法applyGlobalPropertiesIfNecessary(filter)的代码补充:

private void applyGlobalPropertiesIfNecessary(Filter filter) {
        applyLoginUrlIfNecessary(filter);
        applySuccessUrlIfNecessary(filter);
        applyUnauthorizedUrlIfNecessary(filter);
}

该方法内调用了三个方法,但这三个方法只是针对不同对象做了类似的事情而已。以第一个applyLoginUrlIfNecessary(filter)方法为例进行分析:

//请结合下边的XML配置来理解该代码
private void applyLoginUrlIfNecessary(Filter filter) {
    //获取XML中shiroFilter对应的bean中配置的loginUrl属性的值(XML中配置的值为"/login.html")
    String loginUrl = getLoginUrl();
    //此处注意,filter需要为AccessControlFilter或其子类型(AccessControlFilter的继承机构见下方)
    if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
        AccessControlFilter acFilter = (AccessControlFilter) filter;
        //only apply the login url if they haven't explicitly configured one already:
        //获取Filter中配置的loginUrl属性的值(下方XML中配置的值也为"/login.html")
        String existingLoginUrl = acFilter.getLoginUrl();
        //如果该Filter中配置的loginUrl是默认值(/login.jsp)的话,就将它设置为XML中配置的值(以配置为主)
        if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
                acFilter.setLoginUrl(loginUrl);
        }
    }
}

与上边代码相关的XML配置:

<!-- 基于Form表单的身份验证过滤器 -->
    <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="loginUrl" value="/login.html"/>
        <property name="successUrl" value="/"/>
    </bean>

<!-- Shiro的Web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.html"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /assets/** = anon
                /css/** = anon
                /img/** = anon
                /js/** = anon
                /logout = anon
            </value>
        </property>
    </bean>

AccessControlFilter继承结构图如下,从图中可以看到FormAuthenticationFilter是AccessControlFilter的子类:

160730_n9YS_3529605.png

从上边的分析可知,其实我们在XML中对formAuthenticationFilter中loginUrl的配置是多余的,完全可以省略,ShiroFilterFactoryBean会为我们做设置。

applySuccessUrlIfNecessary(filter)与applyUnauthorizedUrlIfNecessary(filter)两个类似,只是针对的对象不同而已,具体内容自行查看代码:

applySuccessUrlIfNecessary(filter):为AuthenticationFilter或其子类设置successUrl(默认路径为"/");

applyUnauthorizedUrlIfNecessary(filter):为AuthorizationFilter或其子类设置unauthorizedUrl(无默认路径)。

 

其次是对开头提到的两篇文章中的第一篇文章的补充:

这篇文章中只提到了Filter生命周期(init() -- doFilter() -- destory())中的init()生命周期,没有提到后两个,这里对doFilter()进行简单的解析(请重点关注代码中的注释),这里会涉及到web.xml中配置的Filter(代码中称为origChain,是这些Filter构成的FilterChain)与Shiro中的Filter执行的先后顺序。

先看一下DelegatingFilterProxy#doFilter()代码:

    @Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				if (this.delegate == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("No WebApplicationContext found: " +
								"no ContextLoaderListener or DispatcherServlet registered?");
					}
					this.delegate = initDelegate(wac);
				}
				delegateToUse = this.delegate;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

    protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}

此处delegate.doFilter()会调用OncePerRequestFilter#doFilter() => AbstractShiroFilter#doFilterInternal() => AbstractShiroFilter#executeChain()

    protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {

        Throwable t = null;

        try {
            //prepareServletRequest()方法会将HttpServletRequest封装成ShiroHttpServletRequest对象
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            //prepareServletResponse()同样:HttpServletResponse => ShiroHttpServletResponse
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);

            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });
        } catch (ExecutionException ex) {
            t = ex.getCause();
        } catch (Throwable throwable) {
            t = throwable;
        }

        if (t != null) {
            if (t instanceof ServletException) {
                throw (ServletException) t;
            }
            if (t instanceof IOException) {
                throw (IOException) t;
            }
            //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one:
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }

    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        //这里返回的是ProxiedFilterChain实例
        FilterChain chain = getExecutionChain(request, response, origChain);
        //执行ProxiedFilterChain#doFilter()方法
        chain.doFilter(request, response);
    }
 
    protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;

        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            return origChain;
        }

        //getChain()方法会根据当前请求路径查找对应的Shiro Filter,如果没有的话会返回origChain对象
        //如果有的话将origChain和Shiro Filter封装成ProxiedFilterChain对象
        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            chain = resolved;
        } else {
        }

        return chain;
    }

 

PathMatchingFilterChainResolver#getChain()

    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }

        //获取请求路径
        String requestURI = getPathWithinApplication(request);

        //the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them
        //as the chain name for the FilterChainManager's requirements
        for (String pathPattern : filterChainManager.getChainNames()) {

            // If the path does match, then pass on to the subclass implementation for specific checks:
            //如果请求路径与FilterChain对应的路径匹配的话执行proxy
            if (pathMatches(pathPattern, requestURI)) {
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }

        return null;
    }

 

DefaultFilterChainManager#proxy()

    public FilterChain proxy(FilterChain original, String chainName) {
        NamedFilterList configured = getChain(chainName);
        if (configured == null) {
            String msg = "There is no configured chain under the name/key [" + chainName + "].";
            throw new IllegalArgumentException(msg);
        }
        return configured.proxy(original);
    }

 

SimpleNamedFilterList#proxy()

    public FilterChain proxy(FilterChain orig) {
        return new ProxiedFilterChain(orig, this);
    }

 

ProxiedFilterChain.java

    /**
     * ProxiedFilterChain的构造方法
     * @Param orig 与web.xml中配置的Filter相对应
     * @Param filters 与Shiro中的Filter相对应
     */
    public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
        if (orig == null) {
            throw new NullPointerException("original FilterChain cannot be null.");
        }
        this.orig = orig;
        this.filters = filters;
        //index默认为0,表示Shiro的FilterChain还为执行,这个值会随着请求路径中对应的Shiro Filter的执行递增,直到所有的Shiro Filter执行完毕
        this.index = 0;
    }

    //执行Filter的doFilter()方法
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        //如果当前请求路径中对应的Filter为空或都已经被执行完毕的话就执行web.xml中配置的Filter
        if (this.filters == null || this.filters.size() == this.index) {
            //we've reached the end of the wrapped chain, so invoke the original one:
            this.orig.doFilter(request, response);
        } else {
            //执行Shiro Filter。注意doFilter的第三个参数是this,即ProxiedFilterChain的当前对象
            //这样当filters中的某个Filter的doFilter方法执行完毕以后就可以再调用PorxiedFilterChain#doFilter()了
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }

从上边的分析可知,当web.xml中也配置了Filter的情况下,Shiro会先执行当前请求路径所对应的Shiro Filter,当这些Shiro Filter都已经执行完毕后才会执行web.xml中配置的Filter

转载于:https://my.oschina.net/u/3529605/blog/1514637

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值