集成ACEGI 进行权限控制
转自:http://acegi-test.sourceforge.net/
一. 简单介绍
1.1 本文目的
集成Acegi到自己的项目中, 并且将用户信息和权限放到数据库, 提供方法允许权限动态变化,变化后自动加载最新的权限
本文介绍Acegi例子的时候采用的是acegi-security-samples-tutorial-1.0.6.war
阅读本文需要对Spring有一定的了解, 如果你还没有接触过, 有些地方可能不容易理解, 这时候可能需要参考本文后附的Spring地址, 先了解一下Spring的基本知识.
本文使用的是Mysql数据库, 如果你使用其他的数据库, 可能需要修改相应的SQL.
1.2 安装与配置
项目主页: http://www.acegisecurity.org/
下载地址: http://sourceforge.net/project/showfiles.php?group_id=104215
解压文件后, 将acegi-security-samples-tutorial-1.0.6.war复制Your_Tomcat_Path/webapps/
启动Tomcat, 访问http://localhost:8080/acegi-security-samples-tutorial-1.0.6/
点击页面上任何一个链接,都需要用户登录后访问, 可以在页面上看到可用的用户名和密码.
二. 开始集成到自己的程序中
2.1 将用户和角色放在数据库中
可能是为了演示方便, 简单的展示Acegi如何控制权限, 而不依赖于任何数据库, ACEGI给出的例子采用InMemoryDaoImpl获取用户信息, 用户和角色信息放在WEB-INF/users.properties 文件中, InMemoryDaoImpl 一次性的从该配置文件中读出用户和角色信息, 格式是: 用户名=密码, 角色名, 如第一行是:
marissa=koala,ROLE_SUPERVISOR
就是说marissa的密码是koala, 并且他的角色是ROLE_SUPERVISOR
对这个文件的解析是通过applicationContext-acegi-security.xml中如下的设置进行的:
<!-- UserDetailsService is the most commonly frequently Acegi Security interface implemented by end users --> <bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> <property name="userProperties"> <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="classpath:users.properties" /> </bean> </property> </bean>
除了InMemoryDaoImpl之外, ACEGI还提供了Jdbc和 ldap的支持, 由于使用数据库进行验证比较常见, 下面仅就jdbc实现做出介绍.
不管是InMemoryDaoImpl还是JdbcDaoImpl都是实现了UserDetailsService接口, 而这个接口里只定义了一个方法: UserDetails loadUserByUsername(String username) 就是根据用户名加载UserDetails对象, UserDetails也是一个接口, 定义了一个用户所需要的基本信息, 包括: username, password, authorities等信息
2.1.1 直接使用JdbcDaoImpl 访问数据库中的用户信息
如果ACEGI提供的信息满足你的需要, 也就是说你只需要用户的username, password等信息, 你可以直接使用ACEGI提供的Schema, 这样, 不需要任何变动, JdbcDaoImpl就可以使用了.
如果你的数据库已经定义好了, 或者不想使用ACEGI提供的Schema,那么你也可以自定义JdbcDaoImpl的查询语句
<property name="usersByUsernameQuery"> <value> SELECT email, password, enabled from user u where email = ? </value> </property> <property name="authoritiesByUsernameQuery"> <value> SELECT u.email, r.role_name FROM user_role ur, user u, role r WHERE ur.user_id = u.user_id and ur.role_id = r.role_id and u.email = ? </value> </property>
2.1.2 扩展JdbcDaoImpl获取更多用户信息
如果上面提到的定制查询SQL语句不能提供足够的灵活性, 那么你可能就需要定义一个JdbcDaoImpl的子类, 如果变动不大, 通过覆盖initMappingSqlQueries方法重新定义MappingSqlQuery的实例. 而如果你需要获取更多信息, 比如userId, companyId等, 那就需要做更多的改动, 第一种改动不大, 所以不具体介绍, 下面以第二种改动为例,介绍如何实现这种需求.
我们需要三张表User, Role, User_Role, 具体的SQL如下:
# # Structure for the `role` table : # DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `role_id` int(11) NOT NULL auto_increment, `role_name` varchar(50) default NULL, `description` varchar(20) default NULL, `enabled` tinyint(1) NOT NULL default '1', PRIMARY KEY (`role_id`) ); # # Structure for the `user` table : # DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `user_id` int(11) NOT NULL auto_increment, `company_id` int(11) default NULL, `email` varchar(200) default NULL, `password` varchar(10) default NULL, `enabled` tinyint(1) default NULL, PRIMARY KEY (`user_id`) ); # # Structure for the `user_role` table : # DROP TABLE IF EXISTS `user_role`; CREATE TABLE `user_role` ( `user_role_id` int(11) NOT NULL auto_increment, `user_id` varchar(50) NOT NULL, `role_id` int(11) NOT NULL, PRIMARY KEY (`user_role_id`) );
前面讲过, UserDetailsService接口中只定义了一个方法: UserDetails loadUserByUsername(String username), UserDetails中不存在我们需要的userId 和companyId等信息, 所以我们首先需要扩展UserDetails接口, 并扩展org.acegisecurity.userdetails.User:
IUserDetails.java
package org.security; import org.acegisecurity.GrantedAuthority; /** * The class <code>IUserDetails</code> extends the org.acegisecurity.userdetails.UserDetails interface, and provides additional userId, companyId information<br><br> * @author wade * @see UserDetails */ public interface IUserDetails extends org.acegisecurity.userdetails.UserDetails{ public int getUserId(); public void setUserId(int user_id); public int getCompanyId(); public void setCompanyId(int company_id); public String getUsername(); public void setUsername(String username); public GrantedAuthority[] getAuthorities(); public void setAuthorities(GrantedAuthority[] authorities); }
UserDetailsImpl.java
package org.security; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.userdetails.User; /** * The class <code>UserDetailsImpl</code> extends the org.acegisecurity.userdetails.User class, and provides additional userId, companyId information * @author wade * * @see IUserDetails, User */ public class UserDetailsImpl extends User implements IUserDetails{ private int user_id; private int company_id; private String username; private GrantedAuthority[] authorities; public UserDetailsImpl(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, GrantedAuthority[] authorities) throws IllegalArgumentException { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); setUsername(username); setAuthorities(authorities); } public UserDetailsImpl(int userid, int companyid, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, GrantedAuthority[] authorities) throws IllegalArgumentException { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); this.user_id = userid; this.company_id = companyid; setUsername(username); setAuthorities(authorities); } public int getUserId() { return user_id; } public void setUserId(int user_id) { this.user_id = user_id; } public int getCompanyId() { return company_id; } public void setCompanyId(int company_id) { this.company_id = company_id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public GrantedAuthority[] getAuthorities() { return authorities; } public void setAuthorities(GrantedAuthority[] authorities) { this.authorities = authorities; } }
到此为止, 我们已经准备好了存放用户信息的类, 下面就开始动手修改取用户数据的代码.
假设我们用下面的SQL取用户信息:
SELECT u.user_id, u.company_id, email, password, enabled FROM role r, user_role ur, user u WHERE r.role_id = ur.role_id and ur.user_id = u.user_id and email = ? limit 1
用下面的SQL取用户具有的Role列表
SELECT u.email, r.role_name FROM user_role ur, user u, role r WHERE ur.user_id = u.user_id and ur.role_id = r.role_id and u.email = ?
我们需要修改的主要是两部分:
1. 取用户和用户角色的MappingSqlQuery, 增加了查询的userId和companyId.
2. loadUserByUsername方法, 修改了返回的对象类型,和很少的内部代码.
AcegiJdbcDaoImpl.java
package org.security.acegi; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.List; import javax.sql.DataSource; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.userdetails.jdbc.JdbcDaoImpl; import org.security.IUserDetails; import org.security.UserDetailsImpl; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.object.MappingSqlQuery; /** * The class AcegiJdbcDaoImpl provides the method to get IUserDetail information from db which contains userId, companyId and UserDetail information. * * @author wade * */ public class AcegiJdbcDaoImpl extends JdbcDaoImpl { public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT u.user_id, u.company_id, email, password, enabled from role r, user_role ur, user u where r.role_id = ur.role_id and ur.user_id = u.user_id and email = ? limit 1"; public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?"; protected MappingSqlQuery rolesByUsernameMapping; protected MappingSqlQuery usersByNameMapping; private String authoritiesByUsernameQuery; private String rolePrefix = ""; private String usersByUsernameQuery; private boolean usernameBasedPrimaryKey = true; public AcegiJdbcDaoImpl(){ usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY; authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY; } public String getAuthoritiesByUsernameQuery() { return authoritiesByUsernameQuery; } public String getRolePrefix() { return rolePrefix; } public String getUsersByUsernameQuery() { return usersByUsernameQuery; } protected void initMappingSqlQueries() { this.usersByNameMapping = new UsersByUsernameMapping(getDataSource()); this.rolesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource()); } /** * Allows the default query string used to retrieve authorities based on username to be overriden, if * default table or column names need to be changed. The default query is {@link * #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped * back to the same column names as in the default query. * * @param queryString The query string to set */ public void setAuthoritiesByUsernameQuery(String queryString) { authoritiesByUsernameQuery = queryString; } /** * Allows a default role prefix to be specified. If this is set to a non-empty value, then it is * automatically prepended to any roles read in from the db. This may for example be used to add the * <code>ROLE_</code> prefix expected to exist in role names (by default) by some other Acegi Security framework * classes, in the case that the prefix is not already present in the db. * * @param rolePrefix the new prefix */ public void setRolePrefix(String rolePrefix) { this.rolePrefix = rolePrefix; } /** * If <code>true</code> (the default), indicates the {@link #getUsersByUsernameQuery()} returns a username * in response to a query. If <code>false</code>, indicates that a primary key is used instead. If set to * <code>true</code>, the class will use the database-derived username in the returned <code>UserDetailsImpl</code>. * If <code>false</code>, the class will use the {@link #loadUserByUsername(String)} derived username in the * returned <code>UserDetailsImpl</code>. * * @param usernameBasedPrimaryKey <code>true</code> if the mapping queries return the username <code>String</code>, * or <code>false</code> if the mapping returns a database primary key. */ public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) { this.usernameBasedPrimaryKey = usernameBasedPrimaryKey; } /** * Allows the default query string used to retrieve users based on username to be overriden, if default * table or column names need to be changed. The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when * modifying this query, ensure that all returned columns are mapped back to the same column names as in the * default query. If the 'enabled' column does not exist in the source db, a permanent true value for this column * may be returned by using a query similar to <br><pre> * "SELECT username,password,'true' as enabled FROM users WHERE username = ?"</pre> * * @param usersByUsernameQueryString The query string to set */ public void setUsersByUsernameQuery(String usersByUsernameQueryString) { this.usersByUsernameQuery = usersByUsernameQueryString; } public IUserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { List users = usersByNameMapping.execute(username); if (users.size() == 0) { throw new UsernameNotFoundException("User not found"); } IUserDetails user = (IUserDetails) users.get(0); // contains no GrantedAuthority[] List dbAuths = rolesByUsernameMapping.execute(user.getUsername()); addCustomAuthorities(user.getUsername(), dbAuths); if (dbAuths.size() == 0) { throw new UsernameNotFoundException("User has no GrantedAuthority"); } GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]); user.setAuthorities(arrayAuths); if (!usernameBasedPrimaryKey) { user.setUsername(username); } return user; } /** * Query object to look up a user's authorities. */ protected class AuthoritiesByUsernameMapping extends MappingSqlQuery { protected AuthoritiesByUsernameMapping(DataSource ds) { super(ds, authoritiesByUsernameQuery); declareParameter(new SqlParameter(Types.VARCHAR)); compile(); } protected Object mapRow(ResultSet rs, int rownum) throws SQLException { String roleName = rolePrefix + rs.getString(2); GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName); return authority; } } /** * Query object to look up a user. */ protected class UsersByUsernameMapping extends MappingSqlQuery { protected UsersByUsernameMapping(DataSource ds) { super(ds, usersByUsernameQuery); declareParameter(new SqlParameter(Types.VARCHAR)); compile(); } protected Object mapRow(ResultSet rs, int rownum) throws SQLException { int user_id = rs.getInt(1); int company_id = rs.getInt(2); String username = rs.getString(3); String password = rs.getString(4); boolean enabled = rs.getBoolean(5); IUserDetails user = new UserDetailsImpl(username, password, enabled, true, true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")}); user.setUserId(user_id); user.setCompanyId(company_id); return user; } } }
修改spring配置, 使用我们新建立的类:
<bean id="userDetailsService" class="org.security.acegi.AcegiJdbcDaoImpl"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="usersByUsernameQuery"> <value> SELECT u.user_id, u.company_id, email, password, enabled from role r, user_role ur, user u where r.role_id = ur.role_id and ur.user_id = u.user_id and email = ? limit 1 </value> </property> <property name="authoritiesByUsernameQuery"> <value> SELECT u.email, r.role_name FROM user_role ur, user u, role r WHERE ur.user_id = u.user_id and ur.role_id = r.role_id and u.email = ? </value> </property> </bean>
好了, 如果再有用户登录,就会调用我们的loadUserByUsername, 从数据库中读取用户数据了, 那用户的权限都有什么呢? 一个用户又对应着哪些ROLE呢? 下面先讲一下ACEGI 例子中的权限设置
2.2 将权限放在数据库中
截止到1.0.6版, Acegi没有提供直接从数据库读取权限的方法, 而是采用通过如下的配置设置权限:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager" /> <property name="accessDecisionManager"> <bean class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value="false" /> <property name="decisionVoters"> <list> <bean class="org.acegisecurity.vote.RoleVoter" /> <bean class="org.acegisecurity.vote.AuthenticatedVoter" /> </list> </property> </bean> </property> <property name="objectDefinitionSource"> <value><![CDATA[ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /secure/extreme/**=ROLE_SUPERVISOR /secure/**=IS_AUTHENTICATED_REMEMBERED /project/**=IS_AUTHENTICATED_REMEMBERED /task/**=ROLE_DEVELOPER /**=IS_AUTHENTICATED_ANONYMOUSLY ]]></value> </property> </bean>
而对大部分项目, 将权限放在数据库中可能是更灵活的, 为此, 我们需要写一个类去读取权限, 为了使这个类尽量简单, 我们把它做成PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap的代理类, PathBasedFilterInvocationDefinitionMap 采用的是Ant Path 风格的匹配方式, 而RegExpBasedFilterInvocationDefinitionMap采用的是Perl5风格的匹配方式. 用户可以通过在配置文件中设置来选择具体比较方式, 默认的比较方式是Ant Path 风格的匹配方式.
这样我们需要做的就是读取权限列表, 并放到相应的代理类里面, 而具体的比较则由代理类进行.
需要的表结构: Resource, Role_Resource
DROP TABLE IF EXISTS `resource`; CREATE TABLE `resource` ( `resource_id` int(11) NOT NULL auto_increment, `parent_resource_id` int(11) default NULL, `resource_name` varchar(50) default NULL, `description` varchar(100) default NULL, PRIMARY KEY (`resource_id`) ); # # Structure for the `resource_role` table : # DROP TABLE IF EXISTS `resource_role`; CREATE TABLE `resource_role` ( `resource_role_id` int(11) NOT NULL auto_increment, `resource_id` int(11) NOT NULL, `role_id` int(11) NOT NULL, PRIMARY KEY (`resource_role_id`) );
添加我们的类:
AcegiJdbcDefinitionSourceImpl.java
package org.security.acegi; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.acegisecurity.ConfigAttributeDefinition; import org.acegisecurity.SecurityConfig; import org.acegisecurity.intercept.web.FilterInvocationDefinitionMap; import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource; import org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap; import org.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.security.IResourceRole; import org.security.ResourceRoleImpl; import org.security.event.IPermissionListener; import org.security.event.PermissionEventPublisher; import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.jdbc.object.MappingSqlQuery; /** * * The class <code>AcegiJdbcDefinitionSourceImpl</code> is proxy to * PathBasedFilterInvocationDefinitionMap or RegExpBasedFilterInvocationDefinitionMap, This class get the permission * settings from the database, the default sql script is: SELECT resource, role * FROM role_permission, if it doesn't match your needs, changed it in bean * setting. <br> * * <br> * $log$<br> * <br> * * @author $Author: wade $ * @see */ public class AcegiJdbcDefinitionSourceImpl extends JdbcDaoSupport implements InitializingBean, FilterInvocationDefinitionSource{ private Log logger = LogFactory.getLog(this.getClass()); public static final String DEF_PERMISSIONS_QUERY = "SELECT resource, role FROM role_permission"; /** The Perl5 expression */ String PERL5_KEY = "PATTERN_TYPE_PERL5"; /** The ant path expression */ String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT"; /* Set default to Ant Path Expression*/ private String resourceExpression = ANT_PATH_KEY; private boolean convertUrlToLowercaseBeforeComparison = false; private FilterInvocationDefinitionMap definitionSource = null; private String permissionsQuery; private String rolePrefix = ""; public AcegiJdbcDefinitionSourceImpl() { permissionsQuery = DEF_PERMISSIONS_QUERY; } public String getAuthoritiesByUsernameQuery() { return permissionsQuery; } public String getRolePrefix() { return rolePrefix; } /** * Allows the default query string used to retrieve permissions to be * overriden, if default table or column names need to be changed. The * default query is {@link #DEF_PERMISSIONS_QUERY}; when modifying this * query, ensure that all returned columns are mapped back to the same * column names as in the default query. * * @param queryString * The query string to set */ public void setPermissionsQuery(String queryString) { permissionsQuery = queryString; } /** * Allows a default role prefix to be specified. If this is set to a * non-empty value, then it is automatically prepended to any roles read in * from the db. This may for example be used to add the <code>ROLE_</code> * prefix expected to exist in role names (by default) by some other Acegi * Security framework classes, in the case that the prefix is not already * present in the db. * * @param rolePrefix * the new prefix */ public void setRolePrefix(String rolePrefix) { this.rolePrefix = rolePrefix; } /** * Init the permission list from db * */ protected void initMap() { // return if we have got the latest permission list if (definitionSource != null) { return; } logger.debug("getting permissions from db"); if (PERL5_KEY.equals(getResourceExpression())) { definitionSource = new RegExpBasedFilterInvocationDefinitionMap(); } else if (ANT_PATH_KEY.equals(getResourceExpression())) { definitionSource = new PathBasedFilterInvocationDefinitionMap(); } else { throw new IllegalArgumentException("wrong resourceExpression value"); } definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison()); MappingSqlQuery permissionsMapping = new PermissionsMapping( getDataSource()); List<IResourceRole> resources = permissionsMapping.execute(); Map<String, String> map = new HashMap<String, String>(); for (int i = 0; i < resources.size(); i++) { ConfigAttributeDefinition defn = new ConfigAttributeDefinition(); String resource = resources.get(i).getResource(); if (map.containsKey(resource)) { continue; } else { map.put(resource, resource); } for (int j = i; j < resources.size(); j++) { IResourceRole resourceRole = resources.get(j); if (resource.equals(resourceRole.getResource())) { defn.addConfigAttribute(new SecurityConfig(resourceRole .getRole())); // logger.debug("added role: " + resourceRole.getRole()); } } definitionSource.addSecureUrl(resources.get(i).getResource(), defn); // logger.debug("added roles to :" + // resources.get(i).getResource()); } } /** * Query object to look up a user's authorities. */ protected class PermissionsMapping extends MappingSqlQuery { protected PermissionsMapping(DataSource ds) { super(ds, permissionsQuery); compile(); } protected IResourceRole mapRow(ResultSet rs, int rownum) throws SQLException { String resource = rs.getString(1); String role = rolePrefix + rs.getString(2); IResourceRole resourceRole = new ResourceRoleImpl(resource, role); return resourceRole; } } public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { initMap(); if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) { return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).getAttributes(object); }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) { return ((PathBasedFilterInvocationDefinitionMap) definitionSource).getAttributes(object); } throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class + " or " + PathBasedFilterInvocationDefinitionMap.class); } public Iterator getConfigAttributeDefinitions() { initMap(); if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) { return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).getConfigAttributeDefinitions(); }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) { return ((PathBasedFilterInvocationDefinitionMap) definitionSource).getConfigAttributeDefinitions(); } throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class + " or " + PathBasedFilterInvocationDefinitionMap.class); } public boolean supports(Class clazz) { initMap(); if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) { return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).supports(clazz); }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) { return ((PathBasedFilterInvocationDefinitionMap) definitionSource).supports(clazz); } throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class + " or " + PathBasedFilterInvocationDefinitionMap.class); } public String getResourceExpression() { return resourceExpression; } public void setResourceExpression(String resourceExpression) { this.resourceExpression = resourceExpression; } public boolean isConvertUrlToLowercaseBeforeComparison() { return convertUrlToLowercaseBeforeComparison; } public void setConvertUrlToLowercaseBeforeComparison( boolean convertUrlToLowercaseBeforeComparison) { this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison; } }
修改spring配置, 使用我们新建立的类和对应的SQL:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager" /> <property name="accessDecisionManager"> <bean class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value="false" /> <property name="decisionVoters"> <list> <bean class="org.acegisecurity.vote.RoleVoter" /> <bean class="org.acegisecurity.vote.AuthenticatedVoter" /> </list> </property> </bean> </property> <property name="objectDefinitionSource"> <ref bean="rolePermissionService"/> </property> </bean> <bean id="rolePermissionService" class="org.security.acegi.AcegiJdbcDefinitionSourceImpl"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="permissionsQuery"> <value> SELECT resource_name, role_name FROM resource_role rr, resource re, role ro WHERE rr.role_id = ro.role_id and rr.resource_id = re.resource_id </value> </property> <property name="convertUrlToLowercaseBeforeComparison" value="false"></property> <property name="resourceExpression" value="PATTERN_TYPE_APACHE_ANT"></property> </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>AcegiP