三
Acegi安全系统扩展
相信side对Acegi的扩展会给你耳目一新的感觉,提供完整的扩展功能,管理界面,中文注释和靠近企业的安全策略。side只对Acegi不符合企业应用需要的功能进行扩展,尽量不改动其余部分来实现全套权限管理功能,以求能更好地适应Acegi升级。
3.1 基于角色的权限控制(RBAC)
Acegi 自带的 sample 表设计很简单: users表{username,password,enabled}
authorities表{username,authority},这样简单的设计无法适应复杂的权限需求,故SpringSide选用RBAC模型对权限控制数据库表进行扩展。
RBAC引入了ROLE的概念,使User(用户)和Permission(权限)分离,一个用户拥有多个角色,一个角色拥有有多个相应的权限,从而减少了权限管理的复杂度,可更灵活地支持安全策略。
同时,我们也引入了resource(资源)的概念,一个资源对应多个权限,资源分为ACL,URL,和FUNTION三种。注意,URL和FUNTION的权限命名需要以AUTH_开头才会有资格参加投票,
同样的ACL权限命名需要ACL_开头。
3.2 管理和使用EhCache
3.2.1 设立缓存
在SpringSide里的 Acegi 扩展使用 EhCache 就作为一种缓存解决方案,以缓存用户和资源的信息和相对应的权限信息。
首先需要一个在classpath的ehcache.xml 文件,用于配置EhCache。
1
< ehcache
>
2
<
defaultCache
3
maxElementsInMemory
="10000"
4
eternal
="false"
5
overflowToDisk
="true"
6
timeToIdleSeconds
="0"
7
timeToLiveSeconds
="0"
8
diskPersistent
="false"
9
diskExpiryThreadIntervalSeconds
= "120"
/>
10
<!--
acegi cache
-->
11
<
cache
name
="userCache"
12
maxElementsInMemory
="10000"
13
eternal
="true"
14
overflowToDisk
= "true"
/>
15
<!--
acegi cache
-->
16
<
cache
name
="resourceCache"
17
maxElementsInMemory
="10000"
18
eternal
="true"
19
overflowToDisk
="true"
/>
20
</
ehcache
>
21
maxElementsInMemory设定了允许在Cache中存放的数据数目,eternal设定Cache是否会过期,overflowToDisk设定内存不足的时候缓存到硬盘,timeToIdleSeconds和timeToLiveSeconds设定缓存游离时间和生存时间,diskExpiryThreadIntervalSeconds设定缓存在硬盘上的生存时间,注意当eternal="true"时,timeToIdleSeconds,timeToLiveSeconds和diskExpiryThreadIntervalSeconds都是无效的.
<defaultCache>是除制定的Cache外其余所有Cache的设置,针对Acegi
的情况, 专门设置了userCache和resourceCache,都设为永不过期。在applicationContext-acegi-security.xml中相应的调用是
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="
userCache"/>
</bean>
<bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"
autowire="byName">
<property name="cache" ref="userCacheBackend"/>
</bean>
<bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="
resourceCache"/>
</bean>
<bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache"
autowire="byName">
<property name="cache" ref="resourceCacheBackend"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
"cacheName" 就是设定在ehcache.xml 中相应Cache的名称
: userCache,resourceCache。
userCache使用的是Acegi 的EhCacheBasedUserCache(实现了UserCache接口)
1
public
interface
UserCache
{
2
public
UserDetails getUserFromCache (String username);
3
public
void
putUserInCache (UserDetails user);
4
public
void
removeUserFromCache (String username);
5
}
UserCache
就是通过EhCache对UserDetails 进行缓存管理
1
public
interface
UserDetails
extends
Serializable
{
2
public
boolean isAccountNonExpired();
3
public
boolean isAccountNonLocked();
4
public
GrantedAuthority[] getAuthorities();
5
public
boolean isCredentialsNonExpired();
6
public
boolean isEnabled();
7
public String getPassword();
8
public String getUsername();
9
}
resourceCache是SpringSide的扩展类
1public class ResourceCache {
2 public ResourceDetails getAuthorityFromCache (String resString) {
3
4 }
5 public void putAuthorityInCache (ResourceDetails resourceDetails) {
6
7 }
8 public void removeAuthorityFromCache (String resString) {
9
10 }
11 public List getUrlResStrings() {
12
13 }
14 public List getFunctions() {
15
16 }
17}
而ResourceCache 是对ResourceDetails 类进行缓存管理
1
public
interface
ResourceDetails
extends
Serializable
{
2
public String getResString();
3
public String getResType();
4
public GrantedAuthority[] getAuthorities();
5
}
GrantedAuthority 就是权限信息,在Acegi 的 sample 里GrantedAuthority
的信息如ROLE_USER, ROLE_SUPERVISOR, ACL_CONTACT_DELETE,
ACL_CONTACT_ADMIN等等,网上也有很多例子把角色作为GrantedAuthority ,但事实上看看ACL
就知道, Acegi本身根本就没有角色这个概念,GrantedAuthority 包含的信息应该是权限,对于非ACL的权限用
AUTH_ 开头更为合理, 如SpringSide里的 AUTH_ADMIN_LOGIN, AUTH_BOOK_MANAGE
等等。
3.2.2 管理缓存
使用AcegiCacheManager对userCache和resourceCache进行统一缓存管理。当在后台对用户信息进行修改或赋权的时候,
在更新数据库同时就会调用acegiCacheManager相应方法, 从数据库中读取数据并替换cache中相应部分,使cache与数据库同步
1
public
class
AcegiCacheManager
extends
BaseService
{
2
private
ResourceCache resourceCache ;
3
private UserCache userCache ;
4
5
/** */
/**
* 修改User时更改userCache
*/
6
public
void
modifyUserInCache (User user, String orgUsername)
{
7
8
}
9
/** */
/**
* 修改Resource时更改resourceCache
*/
10
public
void
modifyResourceInCache (Resource resource, String orgResourcename)
{
11
12
}
13
/** */
/**
* 修改权限时同时修改userCache和resourceCache
*/
14
public
void
modifyPermiInCache (Permission permi, String orgPerminame)
{
15
16
}
17
/** */
/**
* User授予角色时更改userCache
*/
18
public
void authRoleInCache (User user)
{
19
20
}
21
/** */
/**
* Role授予权限时更改userCache和resourceCache
*/
22
public
void
authPermissionInCache (Role role)
{
23
24
}
25
/** */
/**
* Permissioni授予资源时更改resourceCache
*/
26
public
void
authResourceInCache (Permission permi)
{
27
28
}
29
/** */
/** * 初始化userCache
*/
30
public
void initUserCache ()
{
31
32
}
33
/** */
/** * 初始化resourceCache
*/
34
public
void initResourceCache ()
{ }
35
/** */
/** * 获取所有的url资源
*/
36
public List getUrlResStrings ()
{
37
38
}
39
/** */
/** * 获取所有的Funtion资源
*/
40
public List getFunctions ()
{
41
42
}
43
/** */
/** * 根据资源串获取资源
*/
44
public
ResourceDetails getAuthorityFromCache (String resString)
{
45
46
}
47
48
49
50
}
51
3.3 资源权限定义扩展
Acegi给出的sample里,资源权限对照关系是配置在xml中的,试想一下如果你的企业安全应用有500个用户,100个角色权限的时候,维护这个xml将是个繁重无比的工作,如何动态更改用户权限更是个头痛的问题
1
<
bean
id
="contactManagerSecurity"
class
="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"
>
2
<
property
name
="authenticationManager"
>
3
<
ref
bean
="authenticationManager"
/>
4
</
property
>
5
<
property
name
="accessDecisionManager"
>
6
<
ref
local
="businessAccessDecisionManager"
/>
7
</
property
>
8
<
property
name
="afterInvocationManager"
>
9
<
ref
local
="afterInvocationManager"
/>
10
</
property
>
11
<
property
name
="objectDefinitionSource"
>
12
<
value
>
13
sample.contact.ContactManager.create=ROLE_USER
14
sample.contact.ContactManager.getAllRecipients=ROLE_USER
15
sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ
16
sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ
17
sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
18
sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
19
sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
20
</
value
>
21
</
property
>
22
</
bean
>
1
<
bean
id
="filterInvocationInterceptor"
class
="org.acegisecurity.intercept.web.FilterSecurityInterceptor"
>
2
<
property
name
="authenticationManager"
>
3
<
ref
bean
="authenticationManager"
/>
4
</
property
>
5
<
property
name
="accessDecisionManager"
>
6
<
ref
local
="httpRequestAccessDecisionManager"
/>
7
</
property
>
8
<
property
name
="objectDefinitionSource"
>
9
<
value
>
10
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
11
PATTERN_TYPE_APACHE_ANT
12
/index.jsp=ROLE_ANONYMOUS,ROLE_USER
13
/hello.htm=ROLE_ANONYMOUS,ROLE_USER
14
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
15
/switchuser.jsp=ROLE_SUPERVISOR
16
/j_acegi_switch_user=ROLE_SUPERVISOR
17
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
18
/**=ROLE_USER
19
</
value
>
20
</
property
>
21
</
bean
>
22
|