首页 > 代码库 > shiro不重启动态加载权限

shiro不重启动态加载权限

最近一朋友让我帮他做一个后台权限管理的项目。我就在我原来的项目加加改改但是还是不理想,查了不少资料也走了不了弯路。。。。。。

shiro基本的配置我就不多说了这个很简单自己查查资料就完成…………下面是基本的配置不多说,如果这个静态的都不会配置那么就没必要继续往下看了,要稍微了解一点shiro的知识。另外要想动态加载权限的……思路就是重写ShiroFilterFactoryBean类中的setFilterChainDefinitions()方法

<bean id="myShiro" class="com.agnils.base.user.service.MyShiro">  
  <!--  <property name="userService" ref="userService"/>   --> 
  </bean>
    <!-- 配置权限管理器 -->  
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">    
        <!-- ref对应我们写的realm  MyShiro -->  
        <property name="realm" ref="myShiro"/>    
        <!-- 使用下面配置的缓存管理器 -->  
        <property name="cacheManager" ref="cacheManager"/>   
    </bean>  
      
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->  
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">   
        <!-- 调用我们配置的权限管理器 -->   
        <property name="securityManager" ref="securityManager"/>   
        <!-- 配置我们的登录请求地址 -->   
        <property name="loginUrl" value="http://www.mamicode.com/login"/>    
        <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->  
        <property name="successUrl" value="http://www.mamicode.com/index"/>    
        <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->  
        <property name="unauthorizedUrl" value="http://www.mamicode.com/403"/>    
        <!-- 权限配置 -->  
        <property name="filterChainDefinitions">    
            <value>    
                <!-- anon表示此地址不需要任何权限即可访问 -->  
                /static/**=anon  
                /uploadImg/**=anon 
                <!-- perms[user:query]表示访问此连接需要权限为user:query的用户 -->  
                /user=perms[user:query]  
                <!-- roles[manager]表示访问此连接需要用户的角色为manager -->  
                /user/add=roles[manager]
                /user/del/**=roles[admin]  
                /user/edit/**=roles[manager]
                /store/add=anon  
                /store/addStore=roles[manager] 
                /item/sale/**=roles[manager]
                /login=anon 
                /customLogin=anon 
                /ordering/**=anon
                /register=anon
                /register/**=anon
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login-->    
                /wxConfig/**=roles[manager]
                /wechat/**=anon
                /exam/**=anon
                /playVideos/**=anon
                /**=authc  
                /graph/**=roles[admin]
                /webPage/**=roles[admin]
                /table/**=roles[admin]
            </value>    
        </property>    
    </bean> 

下面我开发说正题……

我先是想第一步从db中加载权限配置……

1.先改配置文件

<bean id="chainDefinitionSectionMetaSource"
        class="com.agnils.base.role.service.ChainDefinitionSectionMetaSource">
        <property name="filterChainDefinitions">
            <value>
                /sys/**=roles[admin]
                /user/**=roles[manager]
                /login=anon
                /logout=anon
            </value>
        </property>
    </bean>
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="http://www.mamicode.com/login" />
        <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
        <property name="successUrl" value="http://www.mamicode.com/index" />
        <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
        <property name="unauthorizedUrl" value="http://www.mamicode.com/403" />
        <!-- 权限配置 -->
        <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource"></property>
    </bean>

 

2添加java类

技术分享
  1 package com.agnils.base.role.service;
  2 
  3 import java.text.MessageFormat;
  4 import java.util.List;
  5 import java.util.Map;
  6 
  7 import org.apache.commons.lang.StringUtils;
  8 import org.apache.shiro.config.Ini;
  9 import org.apache.shiro.config.Ini.Section;
 10 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
 11 import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
 12 import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
 13 import org.apache.shiro.web.servlet.AbstractShiroFilter;
 14 import org.springframework.beans.factory.FactoryBean;
 15 import org.springframework.beans.factory.annotation.Autowired;
 16 
 17 import com.agnils.base.role.dao.ResourceDao;
 18 import com.agnils.base.role.entity.Resource;
 19 
 20 /**
 21  * 标题、简要说明. <br>
 22  * 类详细说明.
 23  * <p>
 24  * Copyright: Copyright (c) 2017-5-28 下午11:07:51
 25  * <p>
 26  * 
 27  * @author agnils@foxmail.com
 28  * @version 1.0.0
 29  */
 30 public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section> {
 31 
 32     @javax.annotation.Resource
 33     private ResourceDao resourceDao;
 34     @Autowired
 35     ShiroFilterFactoryBean shiroFilterFactoryBean;
 36 
 37     private String filterChainDefinitions;
 38     
 39 
 40     public static final String PREMISSION_STRING = "roles[\"{0}\"]";
 41 
 42     @Override
 43     public Section getObject() throws Exception {
 44 
 45         List<Resource> list = (List) resourceDao.findAll();
 46         Ini ini = new Ini();
 47         ini.load(filterChainDefinitions);
 48         Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
 49         for (Resource resource : list) {
 50             if (StringUtils.isNotBlank(resource.getValue()) && StringUtils.isNotBlank(resource.getPermission())) {
 51                 section.put(resource.getValue(), MessageFormat.format(PREMISSION_STRING, resource.getPermission()));
 52             }
 53         }
 54         return section;
 55     }
 56 
 57     @Override
 58     public Class<?> getObjectType() {
 59         return this.getClass();
 60     }
 61 
 62     @Override
 63     public boolean isSingleton() {
 64         return false;
 65     }
 66 
 67     public void setFilterChainDefinitions(String filterChainDefinitions) {
 68         this.filterChainDefinitions = filterChainDefinitions;
 69     }
 70     
 71     public void updatePermission() {  
 72           
 73         synchronized (shiroFilterFactoryBean) {  
 74   
 75             AbstractShiroFilter shiroFilter = null;  
 76   
 77             try {  
 78                 shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();  
 79             } catch (Exception e) {  
 80             }  
 81   
 82             // 获取过滤管理器  
 83             PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter  
 84                     .getFilterChainResolver();  
 85             DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();  
 86   
 87             // 清空初始权限配置  
 88             manager.getFilterChains().clear();  
 89             shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();  
 90   
 91             // 重新构建生成  
 92             shiroFilterFactoryBean.setFilterChainDefinitions(filterChainDefinitions);  
 93             Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();  
 94   
 95             for (Map.Entry<String, String> entry : chains.entrySet()) {  
 96                 String url = entry.getKey();  
 97                 String chainDefinition = entry.getValue().trim().replace(" ", "");  
 98                 manager.createChain(url, chainDefinition);  
 99             }  
100   
101         }  
102     } 
103 
104 }
View Code

List<Resource> list = (List) resourceDao.findAll(); 这个就是从自己的权限表中取,动态添加,删除,修改的权限。在这个我就不累赘说了……

按照第一步的配置这样就可以从db中加载权限……但还是一个缺点就更改权限表中的数据时,还是重启才能生效……

 

第二步,这是本博文的重点 动态加载权限

要想实现动态加载就要重写ShiroFilterFactoryBean类代码如下:

技术分享
package com.agnils.base.sys.permissionRole.service;

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

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;

import com.agnils.base.role.dao.ResourceDao;
import com.agnils.base.role.entity.Resource;
import com.agnils.base.user.entity.Permission;

/**
 * 标题、简要说明. <br>
 * 类详细说明.
 * <p>
 * Copyright: Copyright (c) 2017-6-3 下午9:51:02
 * <p>
 * 
 * @author agnils@foxmail.com
 * @version 1.0.0
 */
public class ShiroPermissionFactory extends ShiroFilterFactoryBean {

    public static final String PREMISSION_STRING = "roles[\"{0}\"]";

    // @javax.annotation.Resource
    // private PermissionService permissionService;

    @javax.annotation.Resource
    ResourceDao resourceDao;

    /** 记录配置中的过滤链 */
    public static String filterChainDefinitions = "";//这个要和配置文件中的名称要一样

    /**
     * 初始化设置过滤链
     */
    @Override
    public void setFilterChainDefinitions(String definitions) {
        filterChainDefinitions = definitions;// 记录配置的静态过滤链
        Map<String, String> otherChains = new HashMap<String, String>();
        List<Resource> list = (List) resourceDao.findAll();
        for (Resource resource : list) {
            if (StringUtils.isNotBlank(resource.getValue()) && StringUtils.isNotBlank(resource.getPermission())) {
                otherChains.put(resource.getValue(), MessageFormat.format(PREMISSION_STRING, resource.getPermission()));
            }
        }
        otherChains.put("/**", "authc");
        // 加载配置默认的过滤链
        Ini ini = new Ini();
        ini.load(filterChainDefinitions);
        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

Map<String, String> otherChains = new HashMap<String, String>();
List<Resource> list = (List) resourceDao.findAll();
for (Resource resource : list) {
if (StringUtils.isNotBlank(resource.getValue()) && StringUtils.isNotBlank(resource.getPermission())) {
otherChains.put(resource.getValue(), MessageFormat.format(PREMISSION_STRING, resource.getPermission()));
}
}
otherChains.put("/**", "authc");

这段代码就是从db中取自己配置的权限,每个人的都不一样,需要根据自己的情况修改

2.2 下面还要加一个FilterChainDefinitionsService类

技术分享
package com.agnils.base.sys.permissionRole.service;

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.springframework.stereotype.Service;

/**
 * 标题、简要说明. <br>
 * 类详细说明.
 * <p>
 * Copyright: Copyright (c) 2017-6-3 下午9:49:01
 * <p>
 * 
 * @author agnils@foxmail.com
 * @version 1.0.0
 */
@Service
public class FilterChainDefinitionsService {

    @Resource
    private ShiroPermissionFactory permissionFactory;

    public void reloadFilterChains() {  
            synchronized (permissionFactory) {   //强制同步,控制线程安全  
                AbstractShiroFilter shiroFilter = null;  
      
                try {  
                    shiroFilter = (AbstractShiroFilter) permissionFactory.getObject();  
      
                    PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter  
                            .getFilterChainResolver();  
                    // 过滤管理器  
                    DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();  
                    // 清除权限配置  
                    manager.getFilterChains().clear();  
                    permissionFactory.getFilterChainDefinitionMap().clear();  
                    // 重新设置权限  
                    permissionFactory.setFilterChainDefinitions(ShiroPermissionFactory.filterChainDefinitions);//传入配置中的filterchains  
      
                    Map<String, String> chains = permissionFactory.getFilterChainDefinitionMap();  
                    //重新生成过滤链  
                    if (!CollectionUtils.isEmpty(chains)) {  
                        for (Map.Entry<String, String> chain : chains.entrySet()) {
                            manager.createChain(chain.getKey(), chain.getValue().replace(" ", ""));
                        }
                    }  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        }}
View Code

好代码的都已完成,如果你做到这一步那你调reloadFilterChains()方法 。 在ShiroFilterFactoryBean类的section.putAll(otherChains);方法会报空指针异常……这个问题阻挠我好长时间。最后查了一下资料。问题出现在配置文件中,

2.3 更改配置文件

技术分享
<bean id="myShiro" class="com.agnils.base.user.service.MyShiro">
        <!-- <property name="userService" ref="userService"/> -->
    </bean>
    <!-- 配置权限管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- ref对应我们写的realm MyShiro -->
        <property name="realm" ref="myShiro" />
        <!-- 使用下面配置的缓存管理器 -->
        <property name="cacheManager" ref="cacheManager" />
    </bean>
    <!-- <bean id="chainDefinitionSectionMetaSource"
        class="com.agnils.base.role.service.ChainDefinitionSectionMetaSource">
        <property name="filterChainDefinitions">
            <value>
                /sys/**=roles[admin]
                /user/**=roles[manager]
                /login=anon
                /logout=anon
            </value>
        </property>
    </bean> -->
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <!-- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> -->
    <bean id="shiroFilter" class="com.agnils.base.sys.permissionRole.service.ShiroPermissionFactory">
        <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="http://www.mamicode.com/login" />
        <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
        <property name="successUrl" value="http://www.mamicode.com/index" />
        <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
        <property name="unauthorizedUrl" value="http://www.mamicode.com/403" />
        <!-- 权限配置 -->
        <!-- <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource"></property> -->
        <property name="filterChainDefinitions">
            <value>
                /sys/**=roles[admin]
                /user/**=roles[manager]
                /login=anon
                /logout=anon
            </value>
        </property>
    </bean>
View Code

写了控制类 ReloadController

技术分享
 1 package com.agnils.base.sys.permissionRole.web;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.stereotype.Controller;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 
 9 import com.agnils.base.sys.permissionRole.service.FilterChainDefinitionsService;
10 
11 
12 /**
13  * 标题、简要说明. <br>
14  * 类详细说明.
15  * <p>
16  * Copyright: Copyright (c) 2017-6-3 下午10:44:27
17  * <p>
18  * @author agnils@foxmail.com
19  * @version 1.0.0
20  */
21 @Controller
22 public class ReloadController {
23 
24     @Autowired
25     FilterChainDefinitionsService filterChainDefinitionsService;
26 
27     @RequestMapping(value="http://www.mamicode.com/reloadRole")
28     public void reloadRole(HttpServletRequest request){
29         filterChainDefinitionsService.reloadFilterChains();
30     }
31 }
View Code

删除,添加 db中的表权限自己测试通过,符合自己要求……

这个里我想说一句,本篇没有全部的配置文件,但主要的关于shiro都在里面,本文了不给伸手党写的……因为每个人的表,不一样所以要改的地方……

如遇到问题可以留言或都email联系……

shiro不重启动态加载权限