修改spring配置, 使用我们新建立的类和对应的SQL:
 1 <bean id="filterInvocationInterceptor"
 2 class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
 3 
 4 <property name="authenticationManager"
 5 ref="authenticationManager" />
 6 
 7 <property name="accessDecisionManager">
 8 
 9 <bean class="org.acegisecurity.vote.AffirmativeBased">
10 
11 <property name="allowIfAllAbstainDecisions"
12 value="false" />
13 
14 <property name="decisionVoters">
15 
16 <list>
17 
18 <bean class="org.acegisecurity.vote.RoleVoter" />
19 
20 <bean
21 
22 class="org.acegisecurity.vote.AuthenticatedVoter" />
23 
24 </list>
25 
26 </property>
27 
28 </bean>
29 
30 </property>
31 
32 <property name="objectDefinitionSource">
33 
34 <ref bean="rolePermissionService"/>
35 
36 </property>
37 
38 </bean>
39 
40 <bean id="rolePermissionService"
41 class="org.security.acegi.AcegiJdbcDefinitionSourceImpl">
42 
43 <property name="dataSource">
44 
45 <ref bean="dataSource" />
46 
47 </property>
48 
49 <property name="permissionsQuery">
50 
51 <value>
52 
53 SELECT resource_name, role_name FROM resource_role rr, resource re, role ro
54 
55 WHERE rr.role_id = ro.role_id and rr.resource_id = re.resource_id
56 
57 </value>
58 
59 </property>
60 
61 <property name="convertUrlToLowercaseBeforeComparison" value="false"></property>
62 
63 <property name="resourceExpression" value="PATTERN_TYPE_APACHE_ANT"></property>
64 
65 </bean>

2.3 使用JUnit进行测试

AcegiPermissionTestCase.java
package org.security;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.intercept.web.FilterInvocation;
import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
import org.acegisecurity.intercept.web.FilterSecurityInterceptor;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.security.BaseSpringTestCase;
import org.security.IResourceRole;
import org.security.IUserDetails;
import org.security.ResourceRoleImpl;
import org.security.acegi.AcegiJdbcDaoImpl;
/** * * The class <code>AcegiPermissionTestCase</code> test acegi permission settings<br><br> * $log$<br><br> * @author $Author: wade $ * @version $Revision: 1.0 $ * @see */ public class AcegiPermissionTestCase extends BaseSpringTestCase {
@Autowired
private FilterInvocationDefinitionSource objectDefinitionSource;
@Autowired
private AcegiJdbcDaoImpl userDetailsService;
@Autowired
private FilterSecurityInterceptor filterInvocationInterceptor;
/** * Get Authentication Token by username * @param username * @return Authentication */ protected Authentication getAuthentication(String username){
IUserDetails userDetail = userDetailsService.loadUserByUsername(username);
Authentication authenticated;
if(userDetail.isEnabled()){
authenticated = new UsernamePasswordAuthenticationToken(userDetail, username, userDetail.getAuthorities());
}else{
// authenticated = new AnonymousAuthenticationToken(username, userDetail, userDetail.getAuthorities()); authenticated = new UsernamePasswordAuthenticationToken(null, null, new GrantedAuthority[]{new GrantedAuthorityImpl("ROLE_ANONYMOUS")});
}
return authenticated;
}
/** * get FilterInvocation from the url * @param url * @return FilterInvocation */ protected FilterInvocation getRequestedResource(String url){
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath(url);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterchain = new FilterChain(){
public void doFilter(ServletRequest arg0, ServletResponse arg1)
throws IOException, ServletException {
}};
FilterInvocation object = new FilterInvocation(request, response, filterchain);
return object;
}
/** * throws AccessDeniedException if no permission * @param username * @param uri */ public void checkPermission(boolean shouldHasPermission, String username, String url){
Authentication authenticated = getAuthentication(username);
FilterInvocation object = getRequestedResource(url);
ConfigAttributeDefinition attr = objectDefinitionSource.getAttributes(object);
boolean hasPermission = false;
try{
filterInvocationInterceptor.getAccessDecisionManager().decide(authenticated, object, attr);
hasPermission = true;
}catch(AccessDeniedException e){
hasPermission = false;
}
if(hasPermission){
assertTrue(username + " shouldn't be able to access " + url, shouldHasPermission);
}else{
assertFalse(username + " should be able to access " + url, shouldHasPermission);
}
}
public void testPermissionForAdmin(){
Map<IResourceRole, Boolean> map = new LinkedHashMap<IResourceRole, Boolean>();
map.put(new ResourceRoleImpl("/admin/index.jsp", "admin" ), true);
map.put(new ResourceRoleImpl("/admin/index.jsp", "project" ), false);
map.put(new ResourceRoleImpl("/admin/index.jsp", "dev" ), false);
map.put(new ResourceRoleImpl("/admin/index.jsp", "disabled" ), false);
map.put(new ResourceRoleImpl("/admin", "admin" ), true);
map.put(new ResourceRoleImpl("/admin", "project"), false);
map.put(new ResourceRoleImpl("/admin", "dev" ), false);
map.put(new ResourceRoleImpl("/admin", "disabled"), false);
map.put(new ResourceRoleImpl("/project/index.jsp", "admin" ), true);
map.put(new ResourceRoleImpl("/project/index.jsp", "project"), true);
map.put(new ResourceRoleImpl("/project/index.jsp", "dev" ), false);
map.put(new ResourceRoleImpl("/project/index.jsp", "disabled"), false);
map.put(new ResourceRoleImpl("/project", "admin" ), true);
map.put(new ResourceRoleImpl("/project", "project" ), true);
map.put(new ResourceRoleImpl("/project", "dev" ), false);
map.put(new ResourceRoleImpl("/project", "disabled" ), false);
map.put(new ResourceRoleImpl("/developer/index.jsp", "admin" ), true);
map.put(new ResourceRoleImpl("/developer/index.jsp", "project" ), true);
map.put(new ResourceRoleImpl("/developer/index.jsp", "dev" ), true);
map.put(new ResourceRoleImpl("/developer/index.jsp", "disabled" ), false);
map.put(new ResourceRoleImpl("/developer", "admin" ), true);
map.put(new ResourceRoleImpl("/developer", "project" ), true);
map.put(new ResourceRoleImpl("/developer", "dev" ), true);
map.put(new ResourceRoleImpl("/developer", "disabled" ), false);
map.put(new ResourceRoleImpl("/index.jsp", "admin" ), true);
map.put(new ResourceRoleImpl("/index.jsp", "project"), true);
map.put(new ResourceRoleImpl("/index.jsp", "dev" ), true);
map.put(new ResourceRoleImpl("/index.jsp", "disabled"), true);
map.put(new ResourceRoleImpl("/acegilogin.jsp", "admin" ), true);
map.put(new ResourceRoleImpl("/acegilogin.jsp", "project" ), true);
map.put(new ResourceRoleImpl("/acegilogin.jsp", "dev" ), true);
map.put(new ResourceRoleImpl("/acegilogin.jsp", "disabled" ), true);
Set<IResourceRole> keySet= map.keySet();
Iterator<IResourceRole> ita = keySet.iterator();
while(ita != null && ita.hasNext()){
IResourceRole resourceRole = ita.next();
boolean expectedPermission = map.get(resourceRole);
checkPermission(expectedPermission, resourceRole.getRole(), resourceRole.getResource());
}
}
}

三. 集成之后

3.1 更改数据库中的权限

到目前为止, 一切顺利, 但是有一个问题, 用户如何修改权限, 修改后我们写的类如何能知道权限变了, 需要去重新加载呢? 看来我们需要再加一些代码以便于在权限被修改后能够得到消息, 然后去刷新权限.
为此, 我们使用Observe(观察者) 模式, 在改变权限后, 由改变权限的类通过调用PermissionEventPublisher.update(this.getClass())发出消息说权限变了.
IPermissionListener.java
public interface IPermissionListener {
public void updatePermission(Class eventSource);
}
PermissionEventPublisher.java
 1 package org.security.event;
 2 
 3 import java.util.HashMap;
 4 
 5 import java.util.Iterator;
 6 
 7 import java.util.Map;
 8 
 9 import org.apache.commons.logging.Log;
10 
11 import org.apache.commons.logging.LogFactory;
12 
13 /**
14  * The class PermissionEventPublisher provides a way to notify the IPermissionListener that the permission has been changed.
15  * @author wade
16  *
17  */
18 public class PermissionEventPublisher {
19 
20 private static Log logger = LogFactory.getLog(PermissionEventPublisher.class);
21 
22 private static Map<IPermissionListener, IPermissionListener> observerList =
23 
24 new HashMap<IPermissionListener, IPermissionListener>();
25 
26 /**
27      * Attach a listener for permission event
28      * 
29      * @param subject
30      * @param listener
31      */
32 public static void attach(IPermissionListener listener){
33 
34 observerList.put(listener, listener);
35 
36 if(logger.isDebugEnabled()){
37 
38 logger.debug("Added listener: " + listener.getClass().getName());
39 
40 }
41 
42 }
43 
44 /**
45      * Detatch from the event updater
46      * @param listener
47      */
48 public static void detatch(IPermissionListener listener){
49 
50 observerList.remove(listener);
51 
52 if(logger.isDebugEnabled()){
53 
54 logger.debug("Removeded listener: " + listener.getClass().getName());
55 
56 }
57 
58 }
59 
60 /**
61      * send message to each listener.
62      * @param eventSource
63      */
64 public static void update(Class eventSource){
65 
66 if(logger.isDebugEnabled()){
67 
68 logger.debug("permission changed from "+eventSource.getName());
69 
70 }
71 
72 Iterator<IPermissionListener> ita = observerList.keySet().iterator();
73 
74 while(ita.hasNext()){
75 
76 IPermissionListener permissionListener = ita.next();
77 
78 permissionListener.updatePermission(eventSource);
79 
80 if(logger.isDebugEnabled()){
81 
82 logger.debug("call update for listener=" + permissionListener.getClass().getName());
83 
84 }
85 
86 }
87 
88 }
89 
90 }
修改AcegiJdbcDefinitionSourceImpl.java, 增加updatePermission方法, 在权限变化后进行处理
 1 public class AcegiJdbcDefinitionSourceImpl extends JdbcDaoSupport implements
 2 
 3 InitializingBean, FilterInvocationDefinitionSource, IPermissionListener {
 4 
 5 public AcegiJdbcDefinitionSourceImpl() {
 6 
 7 permissionsQuery = DEF_PERMISSIONS_QUERY;
 8 
 9 //attach to event publisher, so the class can get the notify when permission changes
10 PermissionEventPublisher.attach(this);
11 
12 }
13 
14 /**
15      * Set definitionSource to null, so we can get a refreshed permission list from db
16      */
17 public void updatePermission(Class eventSource) {
18 
19 definitionSource = null;
20 
21 }
22 
23 }

3.2 在程序中获取当前用户

直接从Acegi中取用户信息不太方便, 为了简化获取用户的方法, 可以添加一个类封装对应的逻辑, 然后通过CurrentUser.getUser()直接取到用户信息.
CurrentUser.java
 1 /**
 2      * Get current user which stored in session
 3      * You must set a user when using junit test
 4      * @return IUserDetails
 5      */
 6 public static IUserDetails getUser(){
 7 
 8 //if not in unit test environment, get the current user using acegi
 9 if ((SecurityContextHolder.getContext() == null)
10 
11 || !(SecurityContextHolder.getContext() instanceof SecurityContext)
12 
13 || (((SecurityContext) SecurityContextHolder.getContext())
14 
15 .getAuthentication() == null)) {
16 
17 return null;
18 
19 }
20 
21 Authentication auth = SecurityContextHolder.getContext().getAuthentication();
22 
23 if (auth.getPrincipal() == null) {
24 
25 return null;
26 
27 }
28 
29 IUserDetails user = null;
30 
31 if (auth.getPrincipal() instanceof IUserDetails) {
32 
33 user = (IUserDetails)auth.getPrincipal();
34 
35 }
36 
37 return user;
38 
39 }
40 

3.3 使用Tag来判断用户是否具有某一种Role的权限

有一点一定要注意, 由于Filter的处理有顺序,所以需要将Acegi的Filter放在最前面.
<authz:authorize ifAnyGranted="ROLE_SUPERVISOR, ROLE_ADMINISTRATOR, ROLE_FULLACCESS">
Role in ROLE_SUPERVISOR, ROLE_ADMINISTRATOR, ROLE_FULLACCESS
</authz:authorize>

3.4 添加自己的Tag

Acegi 提供的Tag只能判断当前用户是不是具有某种Role, 不能判断当前用户对某一个URL有没有权限, 由于很多时候需要根据当前用户的权限来控制某些功能是否显示, 比如只有管理员才显示Add或Delete按钮
这是你可以自己写自己的Tag, 为了简单起见, 我们继承jstl的Tag, 比如下面实现两个条件的Tag, Tag的用法如下:
<auth:ifNotAuthrized url="/system/acl.action">如果当前用户没有指定url的权限,显示本部分内容</auth:ifNotAuthrized>
<auth:ifAuthrized url="/system/acl.action">如果当前用户有指定url的权限,显示本部分内容</auth:ifAuthrized>
AuthorizedTag.java