springMVC+mybatis+spring security<三>:使用數據庫管理資源

實際使用中,資源都是保存在數據庫中,而不是在XML中進行配置。

使用數據庫管理資源時,需要實現FilterInvocationSecurityMetadataSource接口:

public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {

	private boolean expire = false;
	
	private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();

	private Map<String, Collection<ConfigAttribute>> requestMap;

	private AntPathMatcher urlMatcher = new AntPathMatcher();

	@Autowired
	private SysResourceService sysResourceService;

	/**
	 * 獲取所有權限集合
	 */
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> set = new HashSet<ConfigAttribute>();
		for (Map.Entry<String, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
			set.addAll(entry.getValue());
		}
		return set;
	}

	/**
	 * 根據request請求獲取訪問資源所需權限
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object obj) throws IllegalArgumentException {
		//刷新資源
		if(isExpire()){
			this.requestMap.clear();
			expire = false;
		}
		//若map爲空,則重新加載
		if(this.requestMap==null || this.requestMap.isEmpty()){
			this.requestMap = bindRequestMap();
		}
		
		String URL = ((FilterInvocation) obj).getRequestUrl();
		if (URL.contains("&")) {
			URL = URL.substring(0, URL.indexOf("&"));
		}
		Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
		for (Map.Entry<String, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
			if (urlMatcher.match(URL, entry.getKey())) {
				attrs = entry.getValue();
				break;
			}
		}
		return attrs;
	}

	@Override
	public boolean supports(Class<?> arg0) {
		return FilterInvocation.class.isAssignableFrom(arg0);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.requestMap = this.bindRequestMap();

	}

	protected Map<String, Collection<ConfigAttribute>> bindRequestMap() {
		Map<String, Collection<ConfigAttribute>> map = new LinkedHashMap<String, Collection<ConfigAttribute>>();

		Map<String, String> sourceMap = this.loadResouece();

		for (Map.Entry<String, String> entry : sourceMap.entrySet()) {
			String key = entry.getKey();
			Collection<ConfigAttribute> attr = new ArrayList<ConfigAttribute>();
			attr = SecurityConfig.createListFromCommaDelimitedString(entry.getValue());
			map.put(key, attr);
		}

		return map;
	}

	/**
	 * 從數據庫中加載權限和資源的對應列表
	 * 
	 * @return
	 */
	private Map<String, String> loadResouece() {
		Map<String, String> map = new LinkedHashMap<String, String>();
		List<SysResource> list = sysResourceService.getAllResRole();
		if (list != null && list.size() > 0) {
			for (SysResource r : list) {
				String path = r.getResourcePath();
				String code = r.getGroupCode();
				if (map.containsKey(path)) {
					String existCode = map.get(path);
					map.put(path, existCode + "," + code);
				} else {
					map.put(path, code);
				}
			}
		}
		return map;
	}

	public boolean isExpire() {
		return expire;
	}
	public void expireNow(){
		this.expire = true;
	}
}

以上代碼主要的做法就是:

  (1)將資源從數據庫中查出來,並和其對應的角色做關聯,封裝到Map<String, Collection<ConfigAttribute>>中;

  (2)每次請求到達過濾器後,根據請求的URL去map中匹配對應的權限(即角色)。


在security的配置文件中做相應配置:自己實現的過濾器要在security的之前執行

<!-- 自定義過濾器,在security的過濾器之前執行 -->
<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
</pre><pre name="code" class="html"><beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
	<beans:property name="securityMetadataSource" ref="securityMetadataSource"></beans:property>
	<beans:property name="accessDecisionManager" ref="accessDecisionManager"></beans:property>
	<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
</beans:bean>

以上步驟完成後,就可以實現對資源URL的過濾。

在實際使用時,還需要注意到的一個問題就是,在每次分配完權限之後,需重啓系統才能生效。這是因爲該類初始化之後將資源map加載到了內存中,數據庫發生了變化,但內存並沒有刷新。

基於以上原理,解決該問題的辦法就是:在每次分配完權限之後,重新加載map,這也是代碼中加入expire屬性的原因。

      // 數據庫權限發生變化,需要刷新內存
	WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(request
			.getServletContext());
	MySecurityMetadataSource source = (MySecurityMetadataSource)wac.getBean("securityMetadataSource");
	source.expireNow();

這樣可以做到即使不用LogOut更不用重啓項目,也可以動態的刷新權限資源。

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