shrio与spring整合的关键bean——ShiroFilterFactoryBean

1、先看看ShiroFilterFactoryBean的继承关系

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor

实现了FactoryBean接口,这是spring中很重要的一个接口,所以我们看看这个类实现的接口方法

 public Object getObject() throws Exception {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
 }

这个instance对象是private AbstractShiroFilter instance;它是spring要代理的filter

2、看createInstance()方法

protected AbstractShiroFilter createInstance() throws Exception {
		//获取shrio的安全管理容器
        SecurityManager securityManager = getSecurityManager();
        
        //获取过滤链管理器,就是shrio的全部过滤器,里面保存着路径和对应的过滤器或者过滤链
        FilterChainManager manager = createFilterChainManager();
        
        //初始化Ant风格的url匹配器,传入上面的过滤链管理器
        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);
        
        //初始化门户过滤器,所有请求都应该经过这个过滤器
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }
protected FilterChainManager createFilterChainManager() {

        DefaultFilterChainManager manager = new DefaultFilterChainManager();
        //shrio自带的filter
        //在org.apache.shiro.web.filter.mgt.DefaultFilter类定义
        Map<String, Filter> defaultFilters = manager.getFilters();
        for (Filter filter : defaultFilters.values()) {
        	//重点,配置我们设置的那三个url给符合的Filter
        	//三个url:private String loginUrl;
    		//		  private String successUrl;
    		//		  private String unauthorizedUrl;
            applyGlobalPropertiesIfNecessary(filter);
        }
		
		//自定义的过滤器,流程和上面一样
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
                applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                manager.addFilter(name, filter, false);
            }
        }
		
		//获取自定义的过滤规则
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                //重点,进行遍历操作,看下面
                manager.createChain(url, chainDefinition);
            }
        }
        return manager;
}
public DefaultFilterChainManager() {
        this.filters = new LinkedHashMap<String, Filter>();
        this.filterChains = new LinkedHashMap<String, NamedFilterList>();
        addDefaultFilters(false);
  }
private void applyGlobalPropertiesIfNecessary(Filter filter) {
		//filter继承自AccessControlFilter才会起效,也可以进行自定义,这会覆盖全局的
        applyLoginUrlIfNecessary(filter);
        //filter继承自AuthenticationFilter才会起效,也可以进行自定义,这会覆盖全局的
        applySuccessUrlIfNecessary(filter);
        //filter继承自AuthorizationFilter才会起效,也可以进行自定义,这会覆盖全局的
        applyUnauthorizedUrlIfNecessary(filter);
 }
public void createChain(String chainName, String chainDefinition) {
		//字符串转数组,逗号分隔
		String[] filterTokens = splitChainDefinition(chainDefinition);
        for (String token : filterTokens) {
        	// 继续解析过滤器,例如:roles[admin,user]
        	// 解析后得到:{"roles", "admin,user"}
        	//roles是shrio默认Filter的其中一个
            String[] nameConfigPair = toNameConfigPair(token);
            //添加过滤链
            addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
        }
 }
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
      
        Filter filter = getFilter(filterName);
        //第一个参数是我们自定义的Ant风格的拦截Url
        //第二个参数是对应的filter
        //第三个参数是拦截规则,如我们配置了"/edit"->"perms[edit]",则参数值是edit
        //看下面
        applyChainConfig(chainName, filter, chainSpecificFilterConfig);
        //对于我们自定义的每一个Ant风格Url,都关联着一个NamedFilterList对象
        NamedFilterList chain = ensureChain(chainName);
        //添加Url对于的filter到这个对象中
        //一个Url可能对应多个filter,所以一个NamedFilterList对象可能不止一个filter
        chain.add(filter);
 }
protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
	  //需要实现PathConfigProcessor接口的filter才能配置上面第三个参数的规则
	  //所以我们在自定义filter的时候尽量继承已有的实现类,或者实现这个接口
      if (filter instanceof PathConfigProcessor) {
     	((PathConfigProcessor) filter).processPathConfig(chainName, 			  chainSpecificFilterConfig);
      } 
 }
protected NamedFilterList ensureChain(String chainName) {
		//尝试获取chain对象,不一定为空,因为之前可能有相同的chainName
        NamedFilterList chain = getChain(chainName);
        if (chain == null) {
            chain = new SimpleNamedFilterList(chainName);
            this.filterChains.put(chainName, chain);
        }
        return chain;
}

FilterChainManager 类的工作是定义全部的filter,包括默认的也包括自定义的,总的思路是一个Ant风格的url设置一个NamedFilterList,它是一个接口,实现类是SimpleNamedFilterList,而一个NamedFilterList对象又可能包括多个filter,这是由我们自定义配置的,我们可能在同一个url设置多个filter,如设置角色权限并且设置操作权限。

那么shrio是怎样拦截请求的呢?
注意SpringShiroFilter这个filter,它是所有请求的入口类
其父类是AbstractShiroFilter实例,注意它的doFilterInternal方法

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

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);
            
            //重点方法
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    //根据request获取对应的chain,进行拦截请求
                    //看下面
                    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;
            }
        }
}
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        //获取chain,进行拦截
        //看下面
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
}
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;
		
		//获取FilterChainResolver对象
		//因为本类构造方法并没有对它进行初始化,所以需要判空
		//为空证明没有对应的FilterChainResolver ,直接返回原来的对象
        FilterChainResolver resolver = getFilterChainResolver();
        if (resolver == null) {
            return origChain;
        }
		
		//resolver对象在这里是PathMatchingFilterChainResolver类的对象
		//看下面
        FilterChain resolved = resolver.getChain(request, response,   origChain);
        if (resolved != null) {
            chain = resolved;
        }
        return chain;
}
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
		//又回到filter管理器类这里来了,默认是DefaultFilterChainManager
		//判断private Map<String, NamedFilterList> filterChains;是否为空
        FilterChainManager filterChainManager = getFilterChainManager();
        //为空则直接返回null,上面就会使用原来的chain
        if (!filterChainManager.hasChains()) {
            return null;
        }
		
		//获取不包括应用名称的请求uri,如/admin
        String requestURI = getPathWithinApplication(request);
        for (String pathPattern : filterChainManager.getChainNames()) {
        	//匹配一个则直接返回
        	//所以我们在自定义uri规则的时候应该考虑顺序问题
            if (pathMatches(pathPattern, requestURI)) {
            	//看下面
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }
        return null;
}
protected boolean pathMatches(String pattern, String path) {
		//默认是AntPathMatcher子类
		//可以配置其他的子类来处理匹配规则
        PatternMatcher pathMatcher = getPathMatcher();
        //匹配方法太过复杂,这里不列出来
        //只需要知道匹配的就返回true
        return pathMatcher.matches(pattern, path);
}
//这是DefaultFilterChainManager方法
public FilterChain proxy(FilterChain original, String chainName) {
		//NamedFilterList是一个接口,实现类只有一个——SimpleNamedFilterList
        NamedFilterList configured = getChain(chainName);
        //实现方法在下面
        return configured.proxy(original);
}
public FilterChain proxy(FilterChain orig) {
        return new ProxiedFilterChain(orig, this);
}
public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
		//原来的chain
        this.orig = orig;
        //NamedFilterList 对象
        this.filters = filters;
        //责任链模式显而易见
        this.index = 0;
}
//ProxiedFilterChain类的doFilter方法
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            this.orig.doFilter(request, response);
        } else {
        	//先使用index,加进行自增
        	//this为自身的引用,责任链模式的模板
            this.filters.get(this.index++).doFilter(request, response, this);
        }
}

总结:shrio与spring整合的ShiroFilterFactoryBean不是一个filter,但是它会初始化一个servlet容器的filter,如果配置的是spring,则需要配置DelegatingFilterProxy来代理这个filter并且拦截的uri必须是大于自定义的包含将要自定义uri规则的路径,否则不会生效,如果是springboot项目,则不需要并且默认拦截/*,直接定义ShiroFilterFactoryBean就行。这个filter其实也只是全部请求的入口,真正进行处理的是shrio默认的filter或者我们自定义的filter。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值