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 閱讀(1651) 評論(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/


          主站蜘蛛池模板: 方城县| 紫云| 谷城县| 石泉县| 辉县市| 丰原市| 丰县| 大埔县| 兴海县| 贵州省| 望谟县| 陈巴尔虎旗| 临沂市| 澎湖县| 灵台县| 阿拉善盟| 拉孜县| 田阳县| 略阳县| 广东省| 太康县| 南丰县| 黄大仙区| 宁南县| 桐庐县| 瑞安市| 文山县| 揭东县| 青海省| 绥德县| 准格尔旗| 浦东新区| 清镇市| 花莲县| 长春市| 新蔡县| 定西市| 台南市| 天气| 佳木斯市| 洛浦县|