shiro(11)-shiroFilter(动态配置过滤器链)

shiro安全控制目录

shiro容器在启动的时候会默认加载一个ini文件,里面有核心的默认配置,当然也有对应的类,当然在实际运用中,我们可以通过查询数据库,将数据放入到InI对象中,动态的进行shiro的配置。

1. 源码引来的思考

一般我们配置shiro主过滤链是如下配置的,将String类型注入到filterChainDefinitions属性中。


    
        
        
        
        
        
        
        
        
            
                
                /login/init/** = anon 
                /login/login/** = anon 
                /static/** = anon     
                /** = user
            
        
        
            
                
            
        
    
    
    

而在源码中,实际上是创建了Ini.section(实际上是一个Map[组件]对象,然后调用了 setFilterChainDefinitionMap(section);方法。那么我们可以自定义一个Ini.section对象(无需在XML中进行配置,可以动态配置过滤器链),然后set注入到filterChainDefinitionMap中呀。

shiro的filterChainDefinitions属性源码配置:

    public void setFilterChainDefinitions(String definitions) {
        Ini ini = new Ini();
        ini.load(definitions);
       
        Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
        if (CollectionUtils.isEmpty(section)) {
            section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        }
        //将设置的Ini.Section保存到FilterChainDefinitionMap中。
        setFilterChainDefinitionMap(section);
    }

传入的definitions值:

                /login/init/** = anon 
                /login/login/** = user 
                /static/** = anon     
                /** = user

2. 动态配置过滤器链

Spring-FactoryBean学习(更灵活的创建bean的方式)

Spring的XML配置文件将对象交由Spring管理的原理是:通过反射机制创建出对象,再交由Spring容器管理。那么我们在代码中若想将Bean注入到容器中,我们可以new出对象来。在调用对象的set方法或者构造方法完成属性注入。

为什么动态配置过滤器链呢?

一般shiro的权限控制原理就是

  1. 实现AuthorizingRealm类,实现里面的钩子方法doGetAuthenticationInfo() 认证回调方法,每次登陆时调用;doGetAuthorizationInfo()授权查询方法,当使用注解 @RequiresPermissions进行权限控制时回调,判断用户是否含有该权限。本质上就是AOP的体现。

  2. 但是通过给每个权限方法加注解,代码比较死板。所以若是可以将权限信息在项目启动的时候,配置在过滤器链中(自定义过滤器链可参考:shiro(8)-shiro过滤器+自定义过滤器)。方式就像授权拦截器一样,/user/**=perms["user:create"],那么就可以舍弃权限标签的用法。

  3. 关键点就是如何动态的获取user:create权限信息。并且默认的perms授权器要求满足所有的权限,才可放行。故我们需要自定义一个过滤器,满足任一权限,便可放行。

1. 自定义授权拦截器myPermissionFilter:满足任一权限便可放行
代码可参考:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

2. 自定义授权拦截器myAuthcFilter:针对上送的请求,进行用户认证(可以在代码中使用getSubject(request, response).login(token);进行用户登录)

如何动态配置过滤器链呢?

1. 方式一:使用@Bean注解

    //读取配置文件中的(自定义的) ShiroFilter链配置
    @Value("${shiro.filterChainDefinitions}")
    private String filterChainDefinitions;

    @Bean
    public Ini.Section section(){
        //自定义过滤器链名称
        String restPermissionString = "myAuthcFilter,myPermissionFilter[{0}]";
        //加载默认的url过滤定义
        Ini ini = new Ini();
        //加载XML配置中的filterChainDefinitions配置
        ini.load(this.filterChainDefinitions);
        //配置Map
        Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        
        List resList =  MyResourceService.queryResourcList(query);
        // 将自定义url过滤添加到section中
        for (MyResourceres : resList) {
            if(StringUtils.isNotBlank(res.getResKey()) && StringUtils.isNotBlank(res.getResUrl())) {
                if(!section.containsKey(res.getResKey())) {
                    section.put(res.getResUrl(), MessageFormat.format(restPermissionString, res.getResKey()));
                }
            }
        }
        section.put("/**", "myAuthcFilter");
        return section;
    }

2. 方式二:使用FactoryBean创建对象

public class ChainDefinitionSectionMetaSource implements FactoryBean
{ @Autowired private SysRescService sysRescService; /** * 默认url过滤定义(shiro过滤器的filterChainDefinitions属性) */ @Value("${shiro.filterChainDefinitions}") private String filterChainDefinitions; /** * 设置默认url过滤定义 * * @param filterChainDefinitions */ public void setFilterChainDefinitions(String filterChainDefinitions) { this.filterChainDefinitions = filterChainDefinitions; } @Override public Section getObject() throws Exception { //自定义过滤器链名称 String restPermissionString = "myAuthcFilter,myPermissionFilter[{0}]"; // 加载默认的url过滤定义 Ini ini = new Ini(); ini.load(this.filterChainDefinitions); Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME); List resList = MyResourceService.queryResourcList(query); // 将自定义url过滤添加到section中 for (MyResourceres : resList) { if(StringUtils.isNotBlank(res.getResKey()) && StringUtils.isNotBlank(res.getResUrl())) { if(!section.containsKey(res.getResKey())) { // /order/query myAuthcFilter,myPermissionFilter[sysuser_list]。/order/query路径访问的情况下,必须经过认证,并且有sysuser_list的权限 section.put(res.getResUrl(), MessageFormat.format(restPermissionString, res.getResKey())); } } } section.put("/**", "myAuthcFilter"); return section; } @Override public Class getObjectType() { return Section.class; } @Override public boolean isSingleton() { return false; } }

灵活的创建出复杂的bean对象后,通过XML将其交由Spring管理。(需要注意的是:即使class参数后面是FactoryBean对象,但是这个Bean,却是InI.section类型。)

  
  
        
            
                
                /login/** = anon 
                /register/** = anon 
                /static/** = anon 
            
        
    


        
        
        
        
        
        
        
            
                
                
            
        
    

文章参考

apache shiro-动态创建filterChainDefinitions

你可能感兴趣的:(shiro(11)-shiroFilter(动态配置过滤器链))