在创建AbstractShiroFilter(shiro过滤器)时,可以配置过滤器,比如:
@Bean("shiroFilter")
@DependsOn({"securityManager"})
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
// 拦截器,放在其他地方任意配置
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 验证码允许匿名访问
filterChainDefinitionMap.put("/**/test1", "anon");
filterChainDefinitionMap.put("/**", "anon");
filterChainDefinitionMap.put("/**/test2/**", "roles[admin,user]");
filterChainDefinitionMap.put("/**/test2/**", "perms[admin,user],roles[admin,user]");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
//自定义filter
Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("loginFilter", new LoginFilter());
shiroFilter.setFilters(filters);
//未授权界面;
//shiroFilter.setUnauthorizedUrl("/unauthorized");
return shiroFilter;
}
但有时需要在AbstractShiroFilter创建之后,任意时刻配值。因为权限过滤器中的权限是在数据库中配置的,在修改数据库中的配置时,也要动态修改内存中的过滤器配置。
在ShiroFilterFactoryBean创建AbstractShiroFilter进入该方法:
org.apache.shiro.spring.web.ShiroFilterFactoryBean#getObject ====>org.apache.shiro.spring.web.ShiroFilterFactoryBean#createInstance====>org.apache.shiro.spring.web.ShiroFilterFactoryBean#createFilterChainManager
可见该方法拿到了配置的过滤器map,最后还设置了默认路径的过滤器。
在shiro最主要的类过滤器管理器DefaultFilterChainManager中
可见filters是过滤器名和过滤器的映射,filterChains是路径和过滤器名的映射。配置过滤器主要就是这2个map
回到org.apache.shiro.spring.web.ShiroFilterFactoryBean#createInstance
可见DefaultFilterChainManager存在了PathMatchingFilterChainResolver中,PathMatchingFilterChainResolver又存在了SpringShiroFilter中,
所以拿到DefaultFilterChainManager在修改里面的filterChains就能随时修改资源和过滤器的映射,注意还要设置默认路径/**。
结合https://blog.youkuaiyun.com/yaoct/article/details/115947781第4节,特别注意org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#applyChainConfig中会在每个过滤器中添加路径,所以每次更新,清空过滤器的配置路径,否则会产生冗余数据
appliedPaths默认不可访问,所以通过反射修改。
参考org.apache.shiro.spring.web.ShiroFilterFactoryBean#createFilterChainManager中的代码,重置访问映射
package com.zjzy.runner;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zjzy.dao.servicebase.SysPermissionServiceBase;
import com.zjzy.dao.servicebase.SysResourcePermissionServiceBase;
import com.zjzy.model.po.SysDept;
import com.zjzy.model.po.SysLog;
import com.zjzy.model.po.SysPermission;
import com.zjzy.model.po.SysResourcePermission;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.filter.mgt.*;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import java.lang.reflect.Field;
import java.util.*;
@Slf4j
@Component
public class ShiroRunner implements ApplicationRunner {
@Autowired
SysResourcePermissionServiceBase sysResourcePermissionServiceBase;
@Autowired
SysPermissionServiceBase sysPermissionServiceBase;
@Autowired
ShiroFilterFactoryBean shiroFilterFactoryBean;
@Autowired
@Qualifier("shiroFilter")
AbstractShiroFilter abstractShiroFilter;
@Override
public void run(ApplicationArguments args) throws Exception {
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver)abstractShiroFilter.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager)filterChainResolver.getFilterChainManager();
Map<String, Filter> filters = manager.getFilters();
//清空appliedPaths
for(Filter filter:filters.values()){
if(filter instanceof PathMatchingFilter){
Class<PathMatchingFilter> pathMatchingFilterClass = PathMatchingFilter.class;
Class<? extends Filter> aClass = filter.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
Field appliedPaths=null;
try{
appliedPaths=PathMatchingFilter.class.getDeclaredField("appliedPaths");
}catch (Exception e){
continue;
}
if(appliedPaths!=null){
appliedPaths.setAccessible(true);
if(appliedPaths.getType().isAssignableFrom(Map.class)){
appliedPaths.set(filter,new LinkedHashMap<String,Object>());
}
appliedPaths.setAccessible(false);
}
}
}
manager.setFilterChains(new LinkedHashMap<>());
// 路径匹配器
Map<String, String> filterChainDefinitionMap = getFilterChainDefinitionMap();
//参考org.apache.shiro.spring.web.ShiroFilterFactoryBean#createFilterChainManager
if (!CollectionUtils.isEmpty(filterChainDefinitionMap)) {
for (Map.Entry<String, String> entry : filterChainDefinitionMap.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
Map<String, NamedFilterList> filterChains = manager.getFilterChains();
// create the default chain, to match anything the path matching would have missed
manager.createDefaultChain("/**"); // TODO this assumes ANT path matching, which might be OK here
}
public Map<String,String> getFilterChainDefinitionMap(){
//匹配数据库
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
List<SysPermission> sysPermissions = sysPermissionServiceBase.list();
List<SysResourcePermission> sysResourcePermissions = sysResourcePermissionServiceBase.list(new LambdaQueryWrapper<SysResourcePermission>().
orderByDesc(SysResourcePermission::getMatchOrder, SysResourcePermission::getOperateTime, SysResourcePermission::getId));
HashMap<Integer,SysPermission> permissionMap=new HashMap<>();
for(SysPermission sysPermission:sysPermissions){
Integer id=sysPermission.getId();
permissionMap.put(id,sysPermission);
}
for(SysResourcePermission sysResourcePermission:sysResourcePermissions){
String path=sysResourcePermission.getPath();
String permissionIds = sysResourcePermission.getPermissionIds();
if(filterChainDefinitionMap.containsKey(path)||permissionIds==null){
continue;
}
StringBuilder sb=new StringBuilder();
String[] split = permissionIds.split(",");
for(String str:split){
try{
Integer num=Integer.valueOf(str);
SysPermission sysPermission = permissionMap.get(num);
if(sysPermission!=null){
sb.append(sysPermission.getCode()+",");
}
}catch (Exception e){
}
}
if(sb.length()>0){
sb.deleteCharAt(sb.length()-1);
filterChainDefinitionMap.put(path,"loginFilter,perms["+sb.toString()+"]");
}
}
return filterChainDefinitionMap;
}
}