最近一朋友讓我幫他做一個后台權限管理的項目。我就在我原來的項目加加改改但是還是不理想,查了不少資料也走了不了彎路。。。。。。
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聯系……