tangtb

Hadoop,SpringMVC,ExtJs,Struts2,Spring,SpringSecurity,Hibernate,Struts
posts - 25, comments - 88, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

【转载】集成ACEGI 进行权限控制

Posted on 2008-08-06 10:41 tangtb 阅读(1629) 评论(0)  编辑  收藏 所属分类: SpringSpring Security

集成ACEGI 进行权限控制

一. 简单介绍

 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中如下的设置进行的:

 1 <!-- UserDetailsService is the most commonly frequently
     Acegi Security interface implemented by end users 
-->
 2 <bean id="userDetailsService"
 3 class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
 4 <property name="userProperties">
 5 <bean
 6 class="org.springframework.beans.factory.config.PropertiesFactoryBean">
 7 <property name="location"
 8 value="classpath:sers.properties" />
 9 </bean>
10 </property>
11 </bean>
12 
13 


除了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的查询语句

 1         <property name="usersByUsernameQuery">
 2 <value>
 3 SELECT email, password, enabled from user u where email = ?
 4 </value>
 5 </property>
 6 <property name="authoritiesByUsernameQuery">
 7 <value>
 8 SELECT u.email, r.role_name FROM user_role ur, user u, role r WHERE
 9 ur.user_id = u.user_id and ur.role_id = r.role_id and u.email = ?
10 </value>
11 </property>

2.1.2 扩展JdbcDaoImpl获取更多用户信息

如果上面提到的定制查询SQL语句不能提供足够的灵活性, 那么你可能就需要定义一个JdbcDaoImpl的子类, 如果变动不大, 通过覆盖initMappingSqlQueries方法重新定义MappingSqlQuery的实例. 而如果你需要获取更多信息, 比如userId, companyId等, 那就需要做更多的改动, 第一种改动不大, 所以不具体介绍, 下面以第二种改动为例,介绍如何实现这种需求.

我们需要三张表User, Role, User_Role, 具体的SQL如下:

 1 1 #
 2  2 # Structure for the `role` table :
 3  3 #
 4  4 DROP TABLE IF EXISTS `role`;
 5  5 CREATE TABLE `role` (
 6  6 `role_id` int(11NOT NULL auto_increment,
 7  7 `role_name` varchar(50default NULL,
 8  8 `description` varchar(20default NULL,
 9  9 `enabled` tinyint(1NOT NULL default '1',
10 10 PRIMARY KEY  (`role_id`)
11 11 );
12 12 #
13 13 # Structure for the `usertable :
14 14 #
15 15 DROP TABLE IF EXISTS `user`;
16 16 CREATE TABLE `user` (
17 17 `user_idint(11NOT NULL auto_increment,
18 18 `company_id` int(11default NULL,
19 19 `email` varchar(200default NULL,
20 20 `password` varchar(10default NULL,
21 21 `enabled` tinyint(1default NULL,
22 22 PRIMARY KEY  (`user_id`)
23 23 );
24 24 #
25 25 # Structure for the `user_role` table :
26 26 #
27 27 DROP TABLE IF EXISTS `user_role`;
28 28 CREATE TABLE `user_role` (
29 29 `user_role_id` int(11NOT NULL auto_increment,
30 30 `user_idvarchar(50NOT NULL,
31 31 `role_id` int(11NOT NULL,
32 32 PRIMARY KEY  (`user_role_id`)
33 33 );

前面讲过, UserDetailsService接口中只定义了一个方法: UserDetails loadUserByUsername(String username), UserDetails中不存在我们需要的userId 和companyId等信息, 所以我们首先需要扩展UserDetails接口, 并扩展org.acegisecurity.userdetails.User:

IUserDetails.java


UserDetailsImpl.java

 1 package org.security;
 2 import org.acegisecurity.GrantedAuthority;
 3 import org.acegisecurity.userdetails.User;
 4 /**
 5  * The class <code>UserDetailsImpl</code> extends the
    * org.acegisecurity.userdetails.User class, and provides
    * additional userId, companyId information
 6  * @author wade
 7  * 
 8  * @see IUserDetails, User
 9  */
10 public class UserDetailsImpl extends User implements IUserDetails{
11 private int user_id;
12 private int company_id;
13 private String username;
14 private GrantedAuthority[] authorities;
15 public UserDetailsImpl(String username, String password, boolean enabled,
16 boolean accountNonExpired, boolean credentialsNonExpired,
17 boolean accountNonLocked, GrantedAuthority[] authorities)
18 throws IllegalArgumentException {
19 super(username, password, enabled, accountNonExpired, credentialsNonExpired,
20 accountNonLocked, authorities);
21 setUsername(username);
22 setAuthorities(authorities);
23 }
24 public UserDetailsImpl(int userid, int companyid, String username,
                                        String password, 
boolean enabled,
25 boolean accountNonExpired, boolean credentialsNonExpired,
26 boolean accountNonLocked, GrantedAuthority[] authorities)
27 throws IllegalArgumentException {
28 super(username, password, enabled, accountNonExpired, credentialsNonExpired,
29 accountNonLocked, authorities);
30 this.user_id = userid;
31 this.company_id = companyid;
32 setUsername(username);
33 setAuthorities(authorities);
34 }
35 public int getUserId() {
36 return user_id;
37 }
38 public void setUserId(int user_id) {
39 this.user_id = user_id;
40 }
41 public int getCompanyId() {
42 return company_id;
43 }
44 public void setCompanyId(int company_id) {
45 this.company_id = company_id;
46 }
47 public String getUsername() {
48 return username;
49 }
50 public void setUsername(String username) {
51 this.username = username;
52 }
53 public GrantedAuthority[] getAuthorities() {
54 return authorities;
55 }
56 public void setAuthorities(GrantedAuthority[] authorities) {
57 this.authorities = authorities;
58 }
59 }


到此为止, 我们已经准备好了存放用户信息的类, 下面就开始动手修改取用户数据的代码.

假设我们用下面的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

  1 package org.security.acegi;
  2 
  3 import java.sql.ResultSet;
  4 
  5 import java.sql.SQLException;
  6 
  7 import java.sql.Types;
  8 
  9 import java.util.List;
 10 
 11 import javax.sql.DataSource;
 12 
 13 import org.acegisecurity.GrantedAuthority;
 14 
 15 import org.acegisecurity.GrantedAuthorityImpl;
 16 
 17 import org.acegisecurity.userdetails.UsernameNotFoundException;
 18 
 19 import org.acegisecurity.userdetails.jdbc.JdbcDaoImpl;
 20 
 21 import org.security.IUserDetails;
 22 
 23 import org.security.UserDetailsImpl;
 24 
 25 import org.springframework.dao.DataAccessException;
 26 
 27 import org.springframework.jdbc.core.SqlParameter;
 28 
 29 import org.springframework.jdbc.object.MappingSqlQuery;
 30 
 31 /**
 32  * The class AcegiJdbcDaoImpl provides the method to
     *  get IUserDetail information from db which contains userId,
     * companyId and UserDetail information.
 33  * 
 34  * @author wade
 35  *
 36  */
 37 public class AcegiJdbcDaoImpl extends JdbcDaoImpl {
 38 
 39 public static final String DEF_USERS_BY_USERNAME_QUERY =
 40 
 41 "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";
 42 
 43 public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
 44 
 45 "SELECT username,authority FROM authorities WHERE username = ?";
 46 
 47 protected MappingSqlQuery rolesByUsernameMapping;
 48 
 49 protected MappingSqlQuery usersByNameMapping;
 50 
 51 private String authoritiesByUsernameQuery;
 52 
 53 private String rolePrefix = "";
 54 
 55 private String usersByUsernameQuery;
 56 
 57 private boolean usernameBasedPrimaryKey = true;
 58 
 59 public AcegiJdbcDaoImpl(){
 60 
 61 usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
 62 
 63 authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
 64 
 65 }
 66 
 67 public String getAuthoritiesByUsernameQuery() {
 68 
 69 return authoritiesByUsernameQuery;
 70 
 71 }
 72 
 73 public String getRolePrefix() {
 74 
 75 return rolePrefix;
 76 
 77 }
 78 
 79 public String getUsersByUsernameQuery() {
 80 
 81 return usersByUsernameQuery;
 82 
 83 }
 84 
 85 protected void initMappingSqlQueries() {
 86 
 87 this.usersByNameMapping = new UsersByUsernameMapping(getDataSource());
 88 
 89 this.rolesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
 90 
 91 }
 92 
 93 /**
 94      * Allows the default query string used to retrieve authorities based on
         *      username to be overriden, if
 95      * default table or column names need to be changed. The default query is {@link
 96      * #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query,
         *      ensure that all returned columns are mapped
 97      * back to the same column names as in the default query.
 98      *
 99      * @param queryString The query string to set
100      */
101 public void setAuthoritiesByUsernameQuery(String queryString) {
102 
103 authoritiesByUsernameQuery = queryString;
104 
105 }
106 
107 /**
108      * Allows a default role prefix to be specified. If this is set to a non-empty value,
         then it is
109      * automatically prepended to any roles read in from the db. This may for example be used to add the
110      * <code>ROLE_</code> prefix expected to exist in role names (by default) by some other Acegi Security framework
111      * classes, in the case that the prefix is not already present in the db.
112      *
113      * @param rolePrefix the new prefix
114      */
115 public void setRolePrefix(String rolePrefix) {
116 
117 this.rolePrefix = rolePrefix;
118 
119 }
120 
121 /**
122      * If <code>true</code> (the default), indicates the {@link #getUsersByUsernameQuery()} returns a username
123      * in response to a query. If <code>false</code>, indicates that a primary key is used instead. If set to
124      * <code>true</code>, the class will use the database-derived username in the returned <code>UserDetailsImpl</code>.
125      * If <code>false</code>, the class will use the {@link #loadUserByUsername(String)} derived username in the
126      * returned <code>UserDetailsImpl</code>.
127      *
128      * @param usernameBasedPrimaryKey <code>true</code> if the mapping queries return the username <code>String</code>,
129      *        or <code>false</code> if the mapping returns a database primary key.
130      */
131 public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
132 
133 this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
134 
135 }
136 
137 /**
138      * Allows the default query string used to retrieve users based on username to be overriden, if default
139      * table or column names need to be changed. The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when
140      * modifying this query, ensure that all returned columns are mapped back to the same column names as in the
141      * default query. If the 'enabled' column does not exist in the source db, a permanent true value for this column
142      * may be returned by using a query similar to <br><pre>
143      * "SELECT username,password,'true' as enabled FROM users WHERE username = ?"</pre>
144      *
145      * @param usersByUsernameQueryString The query string to set
146      */
147 public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
148 
149 this.usersByUsernameQuery = usersByUsernameQueryString;
150 
151 }
152 
153 public IUserDetails loadUserByUsername(String username)
154 
155 throws UsernameNotFoundException, DataAccessException {
156 
157 List users = usersByNameMapping.execute(username);
158 
159 if (users.size() == 0) {
160 
161 throw new UsernameNotFoundException("User not found");
162 
163 }
164 
165 IUserDetails user = (IUserDetails) users.get(0); // contains no GrantedAuthority[]
166 List dbAuths = rolesByUsernameMapping.execute(user.getUsername());
167 addCustomAuthorities(user.getUsername(), dbAuths);
168 if (dbAuths.size() == 0) {
169 
170 throw new UsernameNotFoundException("User has no GrantedAuthority");
171 
172 }
173 
174 GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);
175 
176 user.setAuthorities(arrayAuths);
177 
178 if (!usernameBasedPrimaryKey) {
179 
180 user.setUsername(username);
181 
182 }
183 
184 return user;
185 
186 }
187 
188 /**
189      * Query object to look up a user's authorities.
190      */
191 protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {
192 
193 protected AuthoritiesByUsernameMapping(DataSource ds) {
194 
195 super(ds, authoritiesByUsernameQuery);
196 
197 declareParameter(new SqlParameter(Types.VARCHAR));
198 
199 compile();
200 
201 }
202 
203 protected Object mapRow(ResultSet rs, int rownum)
204 
205 throws SQLException {
206 
207 String roleName = rolePrefix + rs.getString(2);
208 
209 GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
210 
211 return authority;
212 
213 }
214 
215 }
216 
217 /**
218      * Query object to look up a user.
219      */
220 protected class UsersByUsernameMapping extends MappingSqlQuery {
221 
222 protected UsersByUsernameMapping(DataSource ds) {
223 
224 super(ds, usersByUsernameQuery);
225 
226 declareParameter(new SqlParameter(Types.VARCHAR));
227 
228 compile();
229 
230 }
231 
232 protected Object mapRow(ResultSet rs, int rownum)
233 
234 throws SQLException {
235 
236 int user_id = rs.getInt(1);
237 
238 int company_id = rs.getInt(2);
239 
240 String username = rs.getString(3);
241 
242 String password = rs.getString(4);
243 
244 boolean enabled = rs.getBoolean(5);
245 
246 IUserDetails user = new UserDetailsImpl(username, password, enabled, truetruetrue,
247 
248 new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
249 
250 user.setUserId(user_id);
251 
252 user.setCompanyId(company_id);
253 
254 return user;
255 
256 }
257 
258 }
259 
260 }


修改spring配置, 使用我们新建立的类:

 1 <bean id="userDetailsService"
 2 class="org.security.acegi.AcegiJdbcDaoImpl">
 3 <property name="dataSource">
 4 <ref bean="dataSource" />
 5 </property>
 6 <property name="usersByUsernameQuery">
 7 <value>
 8 SELECT u.user_id, u.company_id, email, password, enabled
 9 from role r, user_role ur, user u where r.role_id = ur.role_id and ur.user_id = u.user_id
10 and email = ?
11 limit 1
12 </value>
13 </property>
14 <property name="authoritiesByUsernameQuery">
15 <value>
16 SELECT u.email, r.role_name FROM user_role ur, user u, role r WHERE
17 ur.user_id = u.user_id and ur.role_id = r.role_id and u.email = ?
18 </value>
19 </property>
20 </bean>


好了, 如果再有用户登录,就会调用我们的loadUserByUsername, 从数据库中读取用户数据了, 那用户的权限都有什么呢? 一个用户又对应着哪些ROLE呢? 下面先讲一下ACEGI 例子中的权限设置

2.2 将权限放在数据库中

截止到1.0.6版, Acegi没有提供直接从数据库读取权限的方法, 而是采用通过如下的配置设置权限:

 1     <bean id="filterInvocationInterceptor"
 2 class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
 3 <property name="authenticationManager" ref="authenticationManager" />
 4 <property name="accessDecisionManager">
 5 <bean class="org.acegisecurity.vote.AffirmativeBased">
 6 <property name="allowIfAllAbstainDecisions" value="false" />
 7 <property name="decisionVoters">
 8 <list>
 9 <bean class="org.acegisecurity.vote.RoleVoter" />
10 <bean class="org.acegisecurity.vote.AuthenticatedVoter" />
11 </list>
12 </property>
13 </bean>
14 </property>
15 <property name="objectDefinitionSource">
16 <value><![CDATA[
17 
18 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
19 
20 PATTERN_TYPE_APACHE_ANT
21 
22 /secure/extreme/**=ROLE_SUPERVISOR
23 
24 /secure/**=IS_AUTHENTICATED_REMEMBERED
25 
26 /project/**=IS_AUTHENTICATED_REMEMBERED
27 
28 /task/**=ROLE_DEVELOPER
29 
30 /**=IS_AUTHENTICATED_ANONYMOUSLY
31 
32 ]]></value>
33 </property>
34 </bean>


而对大部分项目, 将权限放在数据库中可能是更灵活的, 为此, 我们需要写一个类去读取权限, 为了使这个类尽量简单, 我们把它做成PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap的代理类, PathBasedFilterInvocationDefinitionMap 采用的是Ant Path 风格的匹配方式, 而RegExpBasedFilterInvocationDefinitionMap采用的是Perl5风格的匹配方式. 用户可以通过在配置文件中设置来选择具体比较方式, 默认的比较方式是Ant Path 风格的匹配方式.

这样我们需要做的就是读取权限列表, 并放到相应的代理类里面, 而具体的比较则由代理类进行.

需要的表结构: Resource, Role_Resource

 1 DROP TABLE IF EXISTS `resource`;
 2 
 3 CREATE TABLE `resource` (
 4 
 5 `resource_id` int(11NOT NULL auto_increment,
 6 
 7 `parent_resource_id` int(11default NULL,
 8 
 9 `resource_name` varchar(50default NULL,
10 
11 `description` varchar(100default NULL,
12 
13 PRIMARY KEY  (`resource_id`)
14 
15 );
16 
17 #
18 
19 # Structure for the `resource_role` table :
20 
21 #
22 
23 DROP TABLE IF EXISTS `resource_role`;
24 
25 CREATE TABLE `resource_role` (
26 
27 `resource_role_id` int(11NOT NULL auto_increment,
28 
29 `resource_id` int(11NOT NULL,
30 
31 `role_id` int(11NOT NULL,
32 
33 PRIMARY KEY  (`resource_role_id`)
34 
35 );


添加我们的类:

AcegiJdbcDefinitionSourceImpl.java

  1 package org.security.acegi;
  2 
  3 import java.sql.ResultSet;
  4 
  5 import java.sql.SQLException;
  6 
  7 import java.util.HashMap;
  8 
  9 import java.util.Iterator;
 10 
 11 import java.util.List;
 12 
 13 import java.util.Map;
 14 
 15 import javax.sql.DataSource;
 16 
 17 import org.acegisecurity.ConfigAttributeDefinition;
 18 
 19 import org.acegisecurity.SecurityConfig;
 20 
 21 import org.acegisecurity.intercept.web.FilterInvocationDefinitionMap;
 22 
 23 import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
 24 
 25 import org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;
 26 
 27 import org.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
 28 
 29 import org.apache.commons.logging.Log;
 30 
 31 import org.apache.commons.logging.LogFactory;
 32 
 33 import org.security.IResourceRole;
 34 
 35 import org.security.ResourceRoleImpl;
 36 
 37 import org.security.event.IPermissionListener;
 38 
 39 import org.security.event.PermissionEventPublisher;
 40 
 41 import org.springframework.beans.factory.InitializingBean;
 42 
 43 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 44 
 45 import org.springframework.jdbc.object.MappingSqlQuery;
 46 
 47 /**
 48  * 
 49  * The class <code>AcegiJdbcDefinitionSourceImpl</code> is proxy to
 50  * PathBasedFilterInvocationDefinitionMap or RegExpBasedFilterInvocationDefinitionMap, This class get the permission
 51  * settings from the database, the default sql script is: SELECT resource, role
 52  * FROM role_permission, if it doesn't match your needs, changed it in bean
 53  * setting. <br>
 54  * 
 55  * <br>
 56  * $log$<br>
 57  * <br>
 58  * 
 59  * @author $Author: wade $
 60  * @see
 61  */
 62 public class AcegiJdbcDefinitionSourceImpl extends JdbcDaoSupport implements
 63 
 64 InitializingBean, FilterInvocationDefinitionSource{
 65 
 66 private Log logger = LogFactory.getLog(this.getClass());
 67 
 68 public static final String DEF_PERMISSIONS_QUERY = "SELECT resource, role FROM role_permission";
 69 
 70 /** The Perl5 expression */
 71 String PERL5_KEY = "PATTERN_TYPE_PERL5";
 72 
 73 /** The ant path expression */
 74 String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT";
 75 
 76 /* Set default to Ant Path Expression*/
 77 private String resourceExpression = ANT_PATH_KEY;
 78 
 79 private boolean convertUrlToLowercaseBeforeComparison = false;
 80 
 81 private FilterInvocationDefinitionMap definitionSource = null;
 82 
 83 private String permissionsQuery;
 84 
 85 private String rolePrefix = "";
 86 
 87 public AcegiJdbcDefinitionSourceImpl() {
 88 
 89 permissionsQuery = DEF_PERMISSIONS_QUERY;
 90 
 91 }
 92 
 93 public String getAuthoritiesByUsernameQuery() {
 94 
 95 return permissionsQuery;
 96 
 97 }
 98 
 99 public String getRolePrefix() {
100 
101 return rolePrefix;
102 
103 }
104 
105 /**
106      * Allows the default query string used to retrieve permissions to be
107      * overriden, if default table or column names need to be changed. The
108      * default query is {@link #DEF_PERMISSIONS_QUERY}; when modifying this
109      * query, ensure that all returned columns are mapped back to the same
110      * column names as in the default query.
111      * 
112      * @param queryString
113      *            The query string to set
114      */
115 public void setPermissionsQuery(String queryString) {
116 
117 permissionsQuery = queryString;
118 
119 }
120 
121 /**
122      * Allows a default role prefix to be specified. If this is set to a
123      * non-empty value, then it is automatically prepended to any roles read in
124      * from the db. This may for example be used to add the <code>ROLE_</code>
125      * prefix expected to exist in role names (by default) by some other Acegi
126      * Security framework classes, in the case that the prefix is not already
127      * present in the db.
128      * 
129      * @param rolePrefix
130      *            the new prefix
131      */
132 public void setRolePrefix(String rolePrefix) {
133 
134 this.rolePrefix = rolePrefix;
135 
136 }
137 
138 /**
139      * Init the permission list from db
140      * 
141      */
142 protected void initMap() {
143 
144 // return if we have got the latest permission list
145 if (definitionSource != null) {
146 
147 return;
148 
149 }
150 
151 logger.debug("getting permissions from db");
152 
153 if (PERL5_KEY.equals(getResourceExpression())) {
154 
155 definitionSource = new RegExpBasedFilterInvocationDefinitionMap();
156 
157 else if (ANT_PATH_KEY.equals(getResourceExpression())) {
158 
159 definitionSource = new PathBasedFilterInvocationDefinitionMap();
160 
161 else {
162 
163 throw new IllegalArgumentException("wrong resourceExpression value");
164 
165 }
166 
167 definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison());
168 
169 MappingSqlQuery permissionsMapping = new PermissionsMapping(
170 
171 getDataSource());
172 
173 List<IResourceRole> resources = permissionsMapping.execute();
174 
175 Map<String, String> map = new HashMap<String, String>();
176 
177 for (int i = 0; i < resources.size(); i++) {
178 
179 ConfigAttributeDefinition defn = new ConfigAttributeDefinition();
180 
181 String resource = resources.get(i).getResource();
182 
183 if (map.containsKey(resource)) {
184 
185 continue;
186 
187 else {
188 
189 map.put(resource, resource);
190 
191 }
192 
193 for (int j = i; j < resources.size(); j++) {
194 
195 IResourceRole resourceRole = resources.get(j);
196 
197 if (resource.equals(resourceRole.getResource())) {
198 
199 defn.addConfigAttribute(new SecurityConfig(resourceRole
200 
201 .getRole()));
202 
203 // logger.debug("added role: " + resourceRole.getRole());
204 }
205 }
206 definitionSource.addSecureUrl(resources.get(i).getResource(), defn);
207 // logger.debug("added roles to :" +
208 // resources.get(i).getResource());
209 }
210 }
211 /**
212      * Query object to look up a user's authorities.
213      */
214 protected class PermissionsMapping extends MappingSqlQuery {
215 
216 protected PermissionsMapping(DataSource ds) {
217 
218 super(ds, permissionsQuery);
219 
220 compile();
221 
222 }
223 
224 protected IResourceRole mapRow(ResultSet rs, int rownum)
225 
226 throws SQLException {
227 
228 String resource = rs.getString(1);
229 
230 String role = rolePrefix + rs.getString(2);
231 
232 IResourceRole resourceRole = new ResourceRoleImpl(resource, role);
233 
234 return resourceRole;
235 
236 }
237 
238 }
239 
240 public ConfigAttributeDefinition getAttributes(Object object)
241 
242 throws IllegalArgumentException {
243 
244 initMap();
245 
246 if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) {
247 
248 return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).getAttributes(object);
249 
250 }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) {
251 
252 return ((PathBasedFilterInvocationDefinitionMap) definitionSource).getAttributes(object);
253 
254 }
255 
256 throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class
257 + " or " + PathBasedFilterInvocationDefinitionMap.class);
258 
259 }
260 
261 public Iterator getConfigAttributeDefinitions() {
262 
263 initMap();
264 
265 if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) {
266 
267 return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).getConfigAttributeDefinitions();
268 
269 }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) {
270 
271 return ((PathBasedFilterInvocationDefinitionMap) definitionSource).getConfigAttributeDefinitions();
272 
273 }
274 
275 throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class
276 + " or " + PathBasedFilterInvocationDefinitionMap.class);
277 
278 }
279 
280 public boolean supports(Class clazz) {
281 
282 initMap();
283 
284 if (definitionSource instanceof RegExpBasedFilterInvocationDefinitionMap) {
285 
286 return ((RegExpBasedFilterInvocationDefinitionMap) definitionSource).supports(clazz);
287 
288 }else if(definitionSource instanceof PathBasedFilterInvocationDefinitionMap) {
289 
290 return ((PathBasedFilterInvocationDefinitionMap) definitionSource).supports(clazz);
291 
292 }
293 
294 throw new IllegalStateException("wrong type of " + definitionSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class
295 + " or " + PathBasedFilterInvocationDefinitionMap.class);
296 
297 }
298 
299 public String getResourceExpression() {
300 
301 return resourceExpression;
302 
303 }
304 
305 public void setResourceExpression(String resourceExpression) {
306 
307 this.resourceExpression = resourceExpression;
308 
309 }
310 
311 public boolean isConvertUrlToLowercaseBeforeComparison() {
312 
313 return convertUrlToLowercaseBeforeComparison;
314 
315 }
316 
317 public void setConvertUrlToLowercaseBeforeComparison(
318 
319 boolean convertUrlToLowercaseBeforeComparison) {
320 
321 this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
322 
323 }
324 
325 }
326 


修改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

  1 public class AuthorizedTag extends ConditionalTagSupport {
  2 
  3 protected Log logger = LogFactory.getLog(this.getClass());
  4 
  5 @Autowired
  6 
  7 private FilterInvocationDefinitionSource objectDefinitionSource;
  8 
  9 @Autowired
 10 
 11 private FilterSecurityInterceptor filterInvocationInterceptor;
 12 
 13 private String url;
 14 
 15 /**
 16      * Get Authentication Token from  IUserDetails object
 17      * @param user
 18      * @return Authentication
 19      */
 20 protected Authentication getAuthentication(IUserDetails user){
 21 
 22 IUserDetails userDetail = user;
 23 
 24 Authentication authenticated;
 25 
 26 if(userDetail == null){
 27 
 28 authenticated = new UsernamePasswordAuthenticationToken(nullnullnew GrantedAuthority[]{new GrantedAuthorityImpl("ROLE_ANONYMOUS")});
 29 
 30 }else{
 31 
 32 if(userDetail.isEnabled()){
 33 
 34 authenticated = new UsernamePasswordAuthenticationToken(userDetail, userDetail.getUsername(), userDetail.getAuthorities());
 35 
 36 }else{
 37 
 38 authenticated = new AnonymousAuthenticationToken(userDetail.getUsername(), userDetail, userDetail.getAuthorities());
 39 
 40 }
 41 
 42 }
 43 
 44 return authenticated;
 45 
 46 }
 47 
 48 /**
 49      * get FilterInvocation from the url 
 50      * @param url
 51      * @return FilterInvocation
 52      */
 53 protected FilterInvocation getRequestedResource(String url){
 54 
 55 MockHttpServletRequest request = new MockHttpServletRequest(pageContext.getServletContext());
 56 
 57 request.setServletPath(url);
 58 
 59 FilterChain filterchain = new FilterChain(){
 60 
 61 public void doFilter(ServletRequest arg0, ServletResponse arg1)
 62 
 63 throws IOException, ServletException {
 64 
 65 }};
 66 
 67 FilterInvocation object = new FilterInvocation(request, pageContext.getResponse(), filterchain);
 68 
 69 return object;
 70 
 71 }
 72 
 73 @Override
 74 
 75 protected boolean condition() throws JspTagException {
 76 
 77 boolean result = false;
 78 
 79 IUserDetails user = CurrentUser.getUser();
 80 
 81 ServletContext servletContext = pageContext.getServletContext();
 82 
 83 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
 84 
 85 wac.getAutowireCapableBeanFactory().autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
 86 
 87 ConfigAttributeDefinition attr = objectDefinitionSource.getAttributes(getRequestedResource(url));
 88 
 89 try{
 90 
 91 filterInvocationInterceptor.getAccessDecisionManager().decide(getAuthentication(user), url, attr);
 92 
 93 result = true;
 94 
 95 }catch(AccessDeniedException e){
 96 
 97 result = false;
 98 
 99 if(user == null){
100 
101 logger.debug("anonymous has no permission on :" + url);
102 
103 }else{
104 
105 logger.debug(user.getUsername() + " has no permission on :" + url);
106 
107 }
108 
109 }
110 
111 return result;
112 
113 }
114 
115 public String getUrl() {
116 
117 return url;
118 
119 }
120 
121 public void setUrl(String url) {
122 
123 this.url = url;
124 
125 }
126 
127 }

添加Jsp页面测试新添加的Tag, 在文所附的例子程序中, 将Tag的测试代码放在index.jsp页面中, 任何人都可以访问该页面, 在页面上列出了全部地址的链接, 同时列出了当前用户有权限的地址, 这样可以方便地知道当前用户有哪些权限, 如果你想修改数据库中的权限, 然后再次测试, 可以点击页面右上侧的Reload Permission重新从数据库加载权限.

<auth:ifAuthrized url="/admin">
<p><a href="admin">Admin page</a></p>
</auth:ifAuthrized>

四. 参考文档

1. 更多深入介绍,可以根据Acegi官方提供的Suggested Steps (http://www.acegisecurity.org/suggested.html) 一步一步学习.

2. 如果要了解Acegi提供的各种功能, 可以参考http://www.acegisecurity.org/reference.html

3. 阅读本文需要对Spring有一定的了解, http://www.springframework.org/documentation

4. 扩展jstl的tag, 可以参看http://www.onjava.com/pub/a/onjava/2002/10/30/jstl3.html?page=1

5. 从https://sourceforge.net/project/platformdownload.php?group_id=216220下载本文附带的例子代码, 通过acegi.sql建立数据库, 然后将acegi-test.war放到Tomcat的webapps目录下, 或者你可以下载acegi-test.zip文件, 里面包含了完整的eclipse的项目以及sql文件.

访问http://youip:port/acegi-test, 列出全部地址的链接, 同时列出了当前用户有权限的地址链接


转自:http://acegi-test.sourceforge.net/



只有注册用户登录后才能发表评论。


网站导航: