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。