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="/login"/> <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 --> <property name="successUrl" value="/index"/> <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 --> <property name="unauthorizedUrl" value="/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="/login" /> <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 --> <property name="successUrl" value="/index" /> <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 --> <property name="unauthorizedUrl" value="/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 }
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); } }
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(); } } }}
好代码的都已完成,如果你做到这一步那你调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="/login" /> <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 --> <property name="successUrl" value="/index" /> <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 --> <property name="unauthorizedUrl" value="/403" /> <!-- 权限配置 --> <!-- <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource"></property> --> <property name="filterChainDefinitions"> <value> /sys/**=roles[admin] /user/**=roles[manager] /login=anon /logout=anon </value> </property> </bean>
写了控制类 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="/reloadRole") 28 public void reloadRole(HttpServletRequest request){ 29 filterChainDefinitionsService.reloadFilterChains(); 30 } 31 }
删除,添加 db中的表权限自己测试通过,符合自己要求……
这个里我想说一句,本篇没有全部的配置文件,但主要的关于shiro都在里面,本文了不给伸手党写的……因为每个人的表,不一样所以要改的地方……
如遇到问题可以留言或都email联系……