shiro的动态权限管理

上次捋shiro底层源码没多久,这次面临了新的需求,如何在实现shiro的权限动态管理。

为什么呢?

我们一般配置shiro,都是这样配置它的过滤器链。而且上次也说过,shiro的拦截器配置从上到下的顺序配置,一旦有一个路径可以包括后边的,那么后边的过滤器就作废了。

当访问路径少的时候,这样是没问题的,但是假如拦截的访问路径很多,这种写在xml配置文件中的数据改起来会很麻烦,而且,每次改动都要重启服务器。

为了解决这个问题,所以提供了shiro的动态权限操作。

所谓动态权限操作,就是把过滤器配置放在数据库中,不论获取还是修改,都是修改数据库的数据,并且实时更新。

核心在于ShiroFilterFactoryBean的setFilterChainDefinitions方法。

根据经验,其实可以分为两部分来看。

1.纯获取

只是单纯的把数据放在数据库中,并不涉及修改。

只需要自己定义一个继承自ShiroFilterFactoryBean的类,重写setFilterChainDefinitions,在这个方法中获取数据库中的数据就可以了。

package org.magicabc.pc.shiro;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.magicabc.bean.Permissions;
import org.magicabc.service.IShiroPermissionsService;
import org.springframework.beans.factory.annotation.Autowired;

/**  
 * @ClassName: ShiroPermissionFactory  
 * @Description: 自定义的的shirofilterfactorybean,setFilterChainDefinitions进行重写,用来从数据库中获取权限相关数据
 * @author yangxu  
 * @date 2018年1月2日  
 *    
 */
public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
    
    /*配置中的过滤链*/  
    public static String definitions;  
      
    /**权限service*/  
    @Autowired  
    private IShiroPermissionsService shiroPermissionsService;  
  
    /** 
     * 初始化设置过滤链 
     */  
    @Override  
    public void setFilterChainDefinitions(String definitions) {  
        ShiroPermissionFactory.definitions = definitions;//记录配置的静态过滤链
          
        //数据库动态权限  
        List<Permissions> permissions = shiroPermissionsService.getPermissions();  
        Map<String, String> otherChains = new HashMap<String, String>();  
        for(Permissions po : permissions){  
            //字符串拼接权限  
           otherChains.put(po.getUrl(), po.getName());
        }  
           otherChains.put("/**", "authc");
        //从配置文件加载权限配置  
        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);  
        }  
          
        //加上数据库中过滤链  
        section.putAll(otherChains);  
        setFilterChainDefinitionMap(section);  
    }  
}
View Code

然后修改配置文件

项目启动时,会自动加载这个ShiroPermissionFactory类,并执行其中的setFilterChainDefinitions方法。

2.动态修改

如果要实现动态修改,需要定义service来实现滤器链和数据库的数据同步

package org.magicabc.pc.shiro;

import java.util.Map;

import javax.annotation.Resource;

import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.magicabc.service.FilterChainDefinitionsService;
import org.springframework.stereotype.Service;

/**  
 * @ClassName: FilterChainDefinitionsService  
 * @Description: 对后台权限数据库进行增删改操作的时候需要调用这个service,用以保证拦截链的同步
 * @author yangxu  
 * @date 2018年1月2日  
 *    
 */
@Service("filterChainDefinitionsService")
public class FilterChainDefinitionsServiceImpl implements FilterChainDefinitionsService{
    @Resource
    private ShiroPermissionFactory shiroPermissionFactory;

    @Override
    public void reloadFilterChains() {
        synchronized(shiroPermissionFactory){
            AbstractShiroFilter shiroFilter = null;
            
            try {
                shiroFilter = (AbstractShiroFilter) shiroPermissionFactory.getObject();
                PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter
                        .getFilterChainResolver();
                // 过滤管理器
                DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();
                // 清除权限配置
                manager.getFilterChains().clear();
                shiroPermissionFactory.getFilterChainDefinitionMap().clear();
                // 重新设置权限
                shiroPermissionFactory.setFilterChainDefinitions(ShiroPermissionFactory.definitions);//传入配置中的filterchains
                Map<String, String> chains = shiroPermissionFactory.getFilterChainDefinitionMap();
                //重新生成过滤链
                if (!CollectionUtils.isEmpty(chains)) {
                    chains.forEach((url, definitionChains) -> {
                        manager.createChain(url, definitionChains.trim().replace(" ", ""));
                    });
                } 
            } catch (Exception e) {
                 e.printStackTrace();  
            }
        }
    }
}
View Code

synchronized代码块保证了强制同步

之后,就是创建自己的Controller,然后在Controller中执行对权限的增删改操作时,需要调用FilterChainDefinitionsServiceImpl中的reloadFilterChains()方法

这样,就对过滤器链进行了动态同步操作。

posted @ 2018-01-02 16:43  一介書生  阅读(1875)  评论(0)    收藏  举报