shiro安全框架擴展教程--如何動態修改資源權限不需要重啓項目

         大家好,感覺好長時間沒有上來更新博客的樣子,因爲上段時間都忙着泡妞,請見諒,最後妞沒泡到,只能繼續傷心研究代碼,之後的幾個文章都是關於shiro框架的,網上關於這個框架的資料都是泛泛而談,沒有跟實際應用結合到一起,然後我把自己的一些應用心得,以及如何擴展該框架適用於我們應用項目的做法,分享給大家...大家賤笑了


        不說笑了,言歸正傳,我不說如何配置基本的架子了,這個大家自行去看吧,網上太多了,我只說關鍵點...


下面看看我的主過濾器配置

<!-- 過濾鏈配置 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/" />
		<property name="successUrl" value="/cms/index.do" />
		<property name="unauthorizedUrl" value="/" />
		<property name="filters">
			<map>
				<entry key="role">
					<bean
						class="com.silvery.security.shiro.filter.RoleAuthorizationFilter" />
				</entry>
				<entry key="authc">
					<bean
						class="com.silvery.security.shiro.filter.SimpleFormAuthenticationFilter" />
				</entry>
			</map>
		</property>
	</bean>

	<!-- 權限資源配置 -->
	<bean id="filterChainDefinitionsService"
		class="com.silvery.security.shiro.service.impl.SimpleFilterChainDefinitionsService">
		<property name="definitions">
			<value>
				/static/** = anon
				/admin/user/login.do = anon
				/test/** = role[user,admin]
				/abc/** = authc
			</value>
		</property>
	</bean>

可以看到我沒有在主過濾器上配置資源,而是自己獨立寫的一個服務類來配置資源,爲何這樣做呢,下面看看我設計的這個實現類


/**
 * 
 * 加載第三方角色資源配置服務類
 * 
 * @author shadow
 * 
 */
public class SimpleFilterChainDefinitionsService extends AbstractFilterChainDefinitionsService {

	@Override
	public Map<String, String> initOtherPermission() {
		// extend to load other permission
		return new HashMap<String, String>();
	}

}

很明顯看到我寫的方法說明,這是加載第三方資源的方法,比如說這裏加載數據庫資源,或者是其他文件上的配置資源,只要把資源讀取出來拼成一個map返回即可,因爲我們都知道shiro最終需要的是一個鍵值對形式的資源


既然這裏是加載第三方資源的,如果沒有第三方資源那直接返回一個空集合即可,那他是如何加載原始的配置資源?因爲我們實際項目都知道一部分的固定資源都會寫在主過濾器那裏,所以考慮到的是,必須要加載一次原始的配置資源,還要再加載一次第三方資源,雙管齊下即可;下面看看我的父級抽象類


/**
 * 
 * 安全框架角色資源配置服務類
 * 
 * @author shadow
 * 
 */
public abstract class AbstractFilterChainDefinitionsService implements FilterChainDefinitionsService {

	private final static Logger log = LoggerFactory.getLogger(AbstractFilterChainDefinitionsService.class);

	private String definitions = "";

	@Autowired
	private ShiroFilterFactoryBean shiroFilterFactoryBean;

	@PostConstruct
	public void intiPermission() {
		shiroFilterFactoryBean.setFilterChainDefinitionMap(obtainPermission());
		log.debug("initialize shiro permission success...");
	}

	public void updatePermission() {

		synchronized (shiroFilterFactoryBean) {

			AbstractShiroFilter shiroFilter = null;

			try {
				shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
			} catch (Exception e) {
				log.error(e.getMessage());
			}

			// 獲取過濾管理器
			PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
					.getFilterChainResolver();
			DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

			// 清空初始權限配置
			manager.getFilterChains().clear();
			shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();

			// 重新構建生成
			shiroFilterFactoryBean.setFilterChainDefinitions(definitions);
			Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();

			for (Map.Entry<String, String> entry : chains.entrySet()) {
				String url = entry.getKey();
				String chainDefinition = entry.getValue().trim().replace(" ", "");
				manager.createChain(url, chainDefinition);
			}

			log.debug("update shiro permission success...");
		}
	}

	/** 讀取配置資源 */
	private Section obtainPermission() {
		Ini ini = new Ini();
		ini.load(definitions); // 加載資源文件節點串
		Section section = ini.getSection("urls"); // 使用默認節點
		if (CollectionUtils.isEmpty(section)) {
			section = ini.getSection(Ini.DEFAULT_SECTION_NAME); // 如不存在默認節點切割,則使用空字符轉換
		}
		Map<String, String> permissionMap = initOtherPermission();
		if (permissionMap != null && !permissionMap.isEmpty()) {
			section.putAll(permissionMap);
		}
		return section;
	}

	public abstract Map<String, String> initOtherPermission();

	public String getDefinitions() {
		return definitions;
	}

	public void setDefinitions(String definitions) {
		this.definitions = definitions;
	}

}

很明顯看到爲何我沒有在主過濾器上配置資源,但是依然會加載資源,是由於我是手動調用了shiroFilterFactoryBean.setFilterChainDefinitionMap(obtainPermission());這個方法自動把資源設置進去;而且obtainPermission()方法中也會調用抽象接口方法加載第三方資源,所以這樣就實現了我的加載原生配置,又能加載第三方資源,這樣設計我覺得比較靈活


下面是另外一個重點,如何更新資源,可參考我寫的updatePermission()方法,主要是清空DefaultFilterChainManager裏面的FilterChains,還有shiroFilterFactoryBean裏面的FilterChainDefinitions;我在網上看了好多關於這個清空資源,但是都不理想,其實是清空這兩個地方,然後重新調用DefaultFilterChainManager的createChain方法,把資源重新設置進去即可,這裏注意下線程安全即可


最後我把接口抽象出來,看看我的頂層接口

public interface FilterChainDefinitionsService {

	public static final String PREMISSION_STRING = "perms[{0}]"; // 資源結構格式
	public static final String ROLE_STRING = "role[{0}]"; // 角色結構格式

	/** 初始化框架權限資源配置 */
	public abstract void intiPermission();

	/** 重新加載框架權限資源配置 (強制線程同步) */
	public abstract void updatePermission();

	/** 初始化第三方權限資源配置 */
	public abstract Map<String, String> initOtherPermission();
}


最後在應用裏的某個地方,從spring中獲取這個接口的實例,然後調用update方法就可以動態更新配置資源


我的shiro框架心得第一個要點分享完了,謝謝大家的香蕉皮,雞蛋殼,如需繼續拍磚,敬請期待下個文章...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章