Shiro系列教程 AccessControlFilter源码分析

AccessControlFilter是shiro-web模块当中比较重要的类,所有的拦截器都继承此类,分析此类源码对应使用其它的filter有很大的帮助。
下图是shiro-web 提供的filter,每种filter都对应了不同的权限拦截规则,本文主要分析AccessControlFilter。



AccessControlFilter的继承关系



ServletContextSupport 源码比较简单不做分析。


AbstractFilter源码分析

public abstract class AbstractFilter extends ServletContextSupport implements Filter {

    private static transient final Logger log = LoggerFactory.getLogger(AbstractFilter.class);


    protected FilterConfig filterConfig;


    public FilterConfig getFilterConfig() {
        return filterConfig;
    }


    public void setFilterConfig(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
        setServletContext(filterConfig.getServletContext());
    }

	//获取filter的配置参数
    protected String getInitParam(String paramName) {
        FilterConfig config = getFilterConfig();
        if (config != null) {
            return StringUtils.clean(config.getInitParameter(paramName));
        }
        return null;
    }

	//filter初始化方法
    public final void init(FilterConfig filterConfig) throws ServletException {
		//设置配置信息对象FilterConfig
        setFilterConfig(filterConfig);
        try {
			//初始化完毕后回调
            onFilterConfigSet();
        } catch (Exception e) {
            if (e instanceof ServletException) {
                throw (ServletException) e;
            } else {
                if (log.isErrorEnabled()) {
                    log.error("Unable to start Filter: [" + e.getMessage() + "].", e);
                }
                throw new ServletException(e);
            }
        }
    }


    protected void onFilterConfigSet() throws Exception {
    }

}

NameableFilter源码分析

public abstract class NameableFilter extends AbstractFilter implements Nameable {


	//给filter加了名字
    private String name;

	//获取名字
    protected String getName() {
		//如果为空则返回web.xml filter-name的值
        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;
    }

    protected StringBuilder toStringBuilder() {
        String name = getName();
        if (name == null) {
            return super.toStringBuilder();
        } else {
            StringBuilder sb = new StringBuilder();
            sb.append(name);
            return sb;
        }
    }

}


OncePerRequestFilter类源码分析

public abstract class OncePerRequestFilter extends NameableFilter {

	//后缀
    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

	//是允许此filter处理
    private boolean enabled = true; //most filters wish to execute when configured, so default to true

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }


    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
		//获得一个key用来标识当前request(请求)已经执行过一次doFilterInternal
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		
		//判断是否已经执行过doFilterInternal
        if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
            log.trace("Filter '{}' already executed.  Proceeding without invoking this filter.", getName());
			//跳过当前filter的处理,直接走后续逻辑(说白了就是脱离shiro的处理)
            filterChain.doFilter(request, response);
        } else //noinspection deprecation
			//判断是否允许执行doFilterInternal 默认是允许的,不走下面的逻辑
            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);

			//执行doFilterInternal逻辑,该方法由子类重写来执行具体的shiro逻辑
            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);
            }
        }
    }


    @SuppressWarnings({"UnusedParameters"})
    protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        return isEnabled();
    }

    protected String getAlreadyFilteredAttributeName() {
        String name = getName();
        if (name == null) {
            name = getClass().getName();
        }
		//名称加上后缀
        return name + ALREADY_FILTERED_SUFFIX;
    }


    @Deprecated
    @SuppressWarnings({"UnusedDeclaration"})
    protected boolean shouldNotFilter(ServletRequest request) throws ServletException {
        return false;
    }

	//由子类重写,这个方法最多被执行一次
    protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException;
}


AdviceFilter类源码fe

//一个AOP的类,在执行chain.doFilter(request, response); 添加了前置 后置 最终三个环绕方法.
public abstract class AdviceFilter extends OncePerRequestFilter {


	//AOP方法
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        return true;
    }


	//AOP方法
    @SuppressWarnings({"UnusedDeclaration"})
    protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
    }


	////AOP方法
    @SuppressWarnings({"UnusedDeclaration"})
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
    }


    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
        chain.doFilter(request, response);
    }

    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        Exception exception = null;

        try {

			//执行前置AOP方法 根据返回值continueChain觉得是否继续执行chain.doFilter(request, response);
            boolean continueChain = preHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");
            }
			
			//如果preHandle返回true则执行
            if (continueChain) {
                executeChain(request, response, chain);
            }
			
			//如果不出异常则执行postHandle
            postHandle(request, response);
            if (log.isTraceEnabled()) {
                log.trace("Successfully invoked postHandle method");
            }

        } catch (Exception e) {
            exception = e;
        } finally {
			//异常与否都在最后执行
            cleanup(request, response, exception);
        }
    }

    protected void cleanup(ServletRequest request, ServletResponse response, Exception existing)
            throws ServletException, IOException {
        Exception exception = existing;
        try {
			//AOP方法
            afterCompletion(request, response, exception);
            if (log.isTraceEnabled()) {
                log.trace("Successfully invoked afterCompletion method.");
            }
        } catch (Exception e) {
            if (exception == null) {
                exception = e;
            } else {
                log.debug("afterCompletion implementation threw an exception.  This will be ignored to " +
                        "allow the original source exception to be propagated.", e);
            }
        }
		//如果executeChain方法出现异常则在这里抛出
        if (exception != null) {
            if (exception instanceof ServletException) {
                throw (ServletException) exception;
            } else if (exception instanceof IOException) {
                throw (IOException) exception;
            } else {
                if (log.isDebugEnabled()) {
                    String msg = "Filter execution resulted in an unexpected Exception " +
                            "(not IOException or ServletException as the Filter API recommends).  " +
                            "Wrapping in ServletException and propagating.";
                    log.debug(msg);
                }
                throw new ServletException(exception);
            }
        }
    }
}


PathMatchingFilter类源码分析
public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {

	//路径匹配器
	//匹配规则http://blog.csdn.net/nimasike/article/details/70739982
    protected PatternMatcher pathMatcher = new AntPathMatcher();

	//这里存的内容是 例如:
	// /login.jsp [anon]
	// /index.jsp [bar, baz]
    protected Map<String, Object> appliedPaths = new LinkedHashMap<String, Object>();

	//假设你的配置是 /user/** = user, roles[admin, foo]
	//如果这个类是roles path为/user/**   config为admin, foo
	//如果这个类是user path为/user/**  config为null
    public Filter processPathConfig(String path, String config) {
        String[] values = null;
        if (config != null) {
            values = split(config);
        }

        this.appliedPaths.put(path, values);
        return this;
    }

    
	//获得请求路径
	//假设请求http://localhost/index.jsp?id=18
	//则返回值为/index.jsp
    protected String getPathWithinApplication(ServletRequest request) {
        return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
    }

    //请求路径与path匹配
    protected boolean pathsMatch(String path, ServletRequest request) {
        String requestURI = getPathWithinApplication(request);
        log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
        return pathsMatch(path, requestURI);
    }

    
    protected boolean pathsMatch(String pattern, String path) {
        return pathMatcher.matches(pattern, path);
    }

    
	//这个方法返回false则请求会被中断
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
            }
            return true;
        }

		//首先进行路径匹配
        for (String path : this.appliedPaths.keySet()) {
            // If the path does match, then pass on to the subclass implementation for specific checks
            //(first match 'wins'):
            if (pathsMatch(path, request)) { 
				//匹配到路径执行isFilterChainContinued
                log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
                Object config = this.appliedPaths.get(path);
                return isFilterChainContinued(request, response, path, config);
            }
        }
		
		//如果没有匹配允许执行
        return true;
    }

   
    @SuppressWarnings({"JavaDoc"})
    private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                           String path, Object pathConfig) throws Exception {

		//这里判断是否允许shiro执行 默认允许
        if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
            if (log.isTraceEnabled()) {
                log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}].  " +
                        "Delegating to subclass implementation for 'onPreHandle' check.",
                        new Object[]{getName(), path, pathConfig});
            }
			//则执行onPreHandle,根据返回值来决定是否继续允许执行后续的filter
			//所有shiro-fiter都会重写此方法,如果返回false 则请求会被中断
            return onPreHandle(request, response, pathConfig);
        }

        if (log.isTraceEnabled()) {
            log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}].  " +
                    "The next element in the FilterChain will be called immediately.",
                    new Object[]{getName(), path, pathConfig});
        }
		
        return true;
    }

   
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return true;
    }

   
    @SuppressWarnings({"UnusedParameters"})
    protected boolean isEnabled(ServletRequest request, ServletResponse response, String path, Object mappedValue)
            throws Exception {
        return isEnabled(request, response);
    }
}


AccessControlFilter类源码
public abstract class AccessControlFilter extends PathMatchingFilter {

	//默认登录页
    public static final String DEFAULT_LOGIN_URL = "/login.jsp";
	
    public static final String GET_METHOD = "GET";

    public static final String POST_METHOD = "POST";

    private String loginUrl = DEFAULT_LOGIN_URL;

    public String getLoginUrl() {
        return loginUrl;
    }

    public void setLoginUrl(String loginUrl) {
        this.loginUrl = loginUrl;
    }

    protected Subject getSubject(ServletRequest request, ServletResponse response) {
        return SecurityUtils.getSubject();
    }

	//子类根据业务规则觉得是否中断请求
    protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return onAccessDenied(request, response);
    }

    protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;

	//这里调用的isAccessAllowed
    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }

    protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
        return pathsMatch(getLoginUrl(), request);
    }

	//保存请求路径调转到登录页面
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        redirectToLogin(request, response);
    }

	//保存请求路径
    protected void saveRequest(ServletRequest request) {
        WebUtils.saveRequest(request);
    }

	//跳转到登录页
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        String loginUrl = getLoginUrl();
        WebUtils.issueRedirect(request, response, loginUrl);
    }

}




猜你喜欢

转载自blog.csdn.net/nimasike/article/details/70807800