Acegi是非常优秀的开源安全框架.Acegi2开始更名为Spring Security.
提供了简化的配置标签.(其实Acegi配置并不复杂,Spring Security为了配置文件上的简洁隐藏了很多底层配置,反而让灵活性下降了一个档次)

Acegi部分:

一,分析:
动态修改权限无非就几种形式,AOP,修改注入Bean,重写底层代码等.
重写底层代码肯定不现实,所以不考虑。AOP的特点是代理,把返回的权限在代理的层面替换掉.因为修改权限不是每次都需要,所以AOP明显的增加了系统开销,所以最后选择修改注入Bean.

附上一段

<bean id="filterSecurityInterceptor" 

class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> 

... 

<property name="objectDefinitionSource"> 

<value> 

CONVERT_URL_TO_UPPERCASE_BEFORE_COMPARISON 

PATTERN_TYPE_APACHE_ANT 

admin.*=User_Admin 

/admin/*=User_Admin 

/allUser.*=User_Customer,User_Admin 

/allUser/*=User_Customer,User_Admin 

</value> 

</property> 

</bean>



很明显,权限的信息和objectDefinitionSource有关.
objectDefinitionSource是接口FilterInvocationDefinitionSource
的某个子类实现,查看API,DefaultFilterInvocationDefinitionSource是唯一被实现的子类.并且提供了入参构造.所以他就是我们下手的目标。

DefaultFilterInvocationDefinitionSource 

(UrlMatcher urlMatcher, LinkedHashMap requestMap) 

urlMatcher表示构造的样式(ANT或者正则) 

LinkedHashMap表示权限的信息


----------------------------------------------------------------------

二,提供资源

既然是动态修改,那就必须提供一个统一的资源注入接口

interface Source { 

public Map<String,String> getSource(); 

} 

Map表示一个资源内容 


提供一个我的实现,这是一个从*.properties读取权限的子类. 

public Map<String, String> getSource() { 

return doReload(); 

} 

protected Map<String, String> doReload() { 

Map<String, String> map = null; 

Boolean isNull = null; 

try { 

Properties pro = PropertiesLoaderUtils.loadAllProperties(proName); 

isNull = pro.size() != 0 ? false : true; 

if (!isNull) { 

map = new LinkedHashMap<String, String>(); 

Iterator<Map.Entry<Object, Object>> it = pro.entrySet().iterator(); 

while (it.hasNext()) { 

Map.Entry entry = it.next(); 

map.put(entry.getKey().toString(), entry.getValue().toString()); 

} 

} 

} 

catch (IOException ex) { 

throw new RuntimeException(ex); 

} 

return map; 

}


这是一个简单的从配置文件读取内容的代码,PropertiesLoaderUtils是Spring提供的工具类

三,构造DefaultFilterInvocationDefinitionSource

DefaultFilterInvocationDefinitionSource需要urlMatcher和LinkedHashMap 

首先,构造LinkedHashMap 

source表示一个Source实现类 

LinkedHashMap<RequestKey, ConfigAttributeDefinition> urlMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>(); 

Iterator<Map.Entry<String, String>> it = source.getSource().entrySet().iterator(); 

while (it.hasNext()){ 

Map.Entry<String, String> entry = it.next(); 

urlMap.put(new RequestKey(entry.getKey()), 

new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(entry.getValue()))); 

} 

StringUtils是Spring提供的工具类用于分割逗号分割的权限 


然后,构造UrlMatcher 

private UrlMatcher createMatcher(boolean useAnt,boolean converUrlToLowerCase) { 

UrlMatcher matcher; 

// 如果是ANT 

if (useAnt) { 

matcher = new AntUrlPathMatcher(); 

((AntUrlPathMatcher) matcher).setRequiresLowerCaseUrl(converUrlToLowerCase); 

} 

// 如果是正则 

else { 

matcher = new RegexUrlPathMatcher(); 

((RegexUrlPathMatcher) matcher).setRequiresLowerCaseUrl(converUrlToLowerCase); 

} 

return matcher; 

}



最后,合并为一个DefaultFilterInvocationDefinitionSource

isDIRECTIVE_PATTERN_TYPE_APACHE_ANT 

Boolean类型,用于表示是否使用大小写转换,默认true 

isDIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 

是Boolean类型,用于表示使用Ant样式,默认true 

DefaultFilterInvocationDefinitionSource(createMatcher(isDIRECTIVE_PATTERN_TYPE_APACHE_ANT,isDIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON),urlMap); 

OK,构造完成.



四,替换
回到

<bean id="filterSecurityInterceptor" 

class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> 

... 

<property name="objectDefinitionSource"> 

<value> 

CONVERT_URL_TO_UPPERCASE_BEFORE_COMPARISON 

PATTERN_TYPE_APACHE_ANT 

admin.*=User_Admin 

/admin/*=User_Admin 

/allUser.*=User_Customer,User_Admin 

/allUser/*=User_Customer,User_Admin 

</value> 

</property> 

</bean> 

filterSecurityInterceptor(FilterSecurityInterceptor)提供了set 

ObjectDefinitionSource.所以可以很方便的注入替换.


----------------------------------------------------------------------

以上基本已经完成了动态修改的核心部分.
考虑到MVC框架如此之多,选了比较流行Struts2+Spring的组合模拟一下场景:

WebRoot/WEB-INF/admin/* 

允许管理员访问 

WebRoot/WEB-INF/allUser/* 

允许管理员和客户访问



从.../项目/login.action开始登陆,进入AllUser的页面
1, 试图进入Admin页面由于权限错误被拦截
2, 返回到AllUser页面,登陆修改权限的页面
3, 返回到AllUser页面,进入Admin页面.可以进入
4, 修改配置文件的权限
5, 返回到AllUser页面,登陆修改权限的页面
6, 试图进入Admin页面由于权限错误被拦截

测试正确

版本信息:
Spring2.5.6
Struts2.1.6
SpringSecurity2.0.4
---------------------------------------------------------------------

由于工作繁忙,基于领域类的动态权限修改和基于注解的领域类动态权限修改稍后再提供.关于SpringSecurity的相关修改方法也会在稍后放出。