zoukankan      html  css  js  c++  java
  • 学习Acegi应用到实际项目中(10)- 保护业务方法

      前面已经讲过关于保护Web资源的方式,其中包括直接在XML文件中配置和自定义实现FilterInvocationDefinitionSource接口两种方式。在实际企业应用中,保护Web资源非常重要,它是保障Web应用安全性的关键部分。有了它,我们的Web应用就显得更加安全了。的确,部分Web应用有了它已经足够了。但许多时候却有这样的场景,某企业的系统允许用户A查看数据,但不允许他修改或删除数据;而用户B不但可以查看数据,而且可以修改和删除数据。此时,前面所说的保护Web资源的方式就无法满足这个需求了。既而我们会想到,关于查看、修改和删除等操作,都是通过操作相应业务方法来实现的。那么,我们可不可以实现对这些业务方法的保护呢?答案是肯定的,Acegi为我们提供了这一实现机制。
      对于业务方法的保护,其实跟保护Web资源的方式非常相似。只要弄清楚了保护Web资源的工作原理和各种实现方式,再来学习保护业务方法相关的知识,那么很快上手。

      在继续阅读本节内容之前,朋友们应该先阅读“学习Acegi应用到实际项目中(9)- 实现FilterInvocationDefinition”一节()。因为此篇的第二部分内容与前一篇的内容非常相似,故在此只列出不同部分,不做详细解释。

    一、在Acegi配置文件中配置实现保护业务方法
    1、下面先看看关于保护Web资源和保护业务方法的部分配置:
      保护Web资源的配置

    <bean id="filterInvocationInterceptor"  
        class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">  
        <property name="authenticationManager" ref="authenticationManager" />  
        <property name="accessDecisionManager"  
            ref="httpRequestAccessDecisionManager" />  
        <property name="objectDefinitionSource">  
            <value>  
                <![CDATA[ 
                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
                    PATTERN_TYPE_APACHE_ANT 
                    /**/*.jpg=AUTH_ANONYMOUS,AUTH_USER 
                    /**/*.gif=AUTH_ANONYMOUS,AUTH_USER 
                    /**/*.png=AUTH_ANONYMOUS,AUTH_USER 
                    /login.jsp*=AUTH_ANONYMOUS,AUTH_USER 
                    /**=AUTH_USER 
                ]]>  
            </value>  
        </property>  
    </bean>

      保护业务方法配置

    <bean id="contactManagerSecurity"  
        class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">  
        <property name="authenticationManager" ref="authenticationManager" />  
        <property name="accessDecisionManager"  
            ref="httpRequestAccessDecisionManager" />  
        <property name="objectDefinitionSource">  
            <value>  
                sample.service.IContactManager.create=AUTH_FUNC_ContactManager.create  
                sample.service.IContactManager.delete=AUTH_FUNC_ContactManager.delete  
                sample.service.IContactManager.getAll=AUTH_FUNC_ContactManager.getAll  
                sample.service.IContactManager.getById=AUTH_FUNC_ContactManager.getById  
                sample.service.IContactManager.update=AUTH_FUNC_ContactManager.update  
            </value>  
        </property>  
    </bean> 

      从上面配置方式可以看到,两种配置方式基本差不多,只有两个地方存在差别。一个是实现类不同,前者是FilterSecurityInterceptor,后者是MethodSecurityInterceptor。另外一个是objectDefinitionSource中的配置不同。
      FilterSecurityInterceptor和MethodSecurityInterceptor都继承自AbstractSecurityInterceptor,而且都拥有objectDefinitionSource属性。尽管他们都拥有相同的objectDefinitionSource属性,但前者属于FilterInvocationDefinitionSource类型,而后者属于MethodDefinitionSource类型。然而,这两个Source又都继承自ObjectDefinitionSource。正因为如此,所以保护Web资源和保护业务方法的原理是一样的,只要懂得运用其中一个,那么另一个也就会了。

      “=”号左边的内容代表了方法名全称,即以类的全限定名和目标方法名一同构成。“=”号右边的内容代表了左边方法所对应的角色集合,角色集合由逗号隔开的多个角色名构成。

    2、业务接口和实现类

    public interface IContactManager{  
      public List getAll();  
      public Contact getById(Integer id);  
      public void create(Contact contact);  
      public void update(Contact contact);  
      public void delete(Contact contact);  
    }  
      
    public class ContactManager implements IContactManager {  
      ……  
    } 

    3、加入保护业务方法的拦截器 

    <bean id="transactionInterceptor"  
        class="org.springframework.transaction.interceptor.TransactionInterceptor">  
        <property name="transactionManager">  
            <ref bean="transactionManager" />  
        </property>  
        <property name="transactionAttributeSource">  
            <value>  
                sample.service.impl.ContactManager.*=PROPAGATION_REQUIRED  
            </value>  
        </property>  
    </bean>  
      
    <!--dao start -->  
    <bean id="contactDao" class="sample.dao.impl.ContactDao">  
        <property name="dataSource">  
            <ref bean="dataSource" />  
        </property>  
    </bean>  
      
    <!--service start -->  
    <bean id="contactManagerTarget"  
        class="sample.service.impl.ContactManager">  
        <property name="contactDao">  
            <ref bean="contactDao" />  
        </property>  
    </bean>  
      
    <bean id="contactManager"  
        class="org.springframework.aop.framework.ProxyFactoryBean">  
        <property name="proxyInterfaces">  
            <value>sample.service.IContactManager</value>  
        </property>  
        <property name="interceptorNames">  
            <list>  
                <idref local="transactionInterceptor" />  
                <strong><!-- 加入保护业务方法的拦截器 -->  
                <idref bean="contactManagerSecurity"/></strong>  
                <idref local="contactManagerTarget" />  
            </list>  
        </property>  
    </bean>  

    二、自定义实现MethodDefinitionSource接口保护业务方法
      这部分内容建立在前一篇的基础之上,请参考:“学习Acegi应用到实际项目中(9)- 实现FilterInvocationDefinition”一节()

    1、修改RdbmsEntryHolder类
      前篇保护Web资源时RdbmsEntryHolder类如下:

    public class RdbmsEntryHolder implements Serializable {  
        // 保护的URL模式     
        private String url;     
        // 要求的角色集合     
        private ConfigAttributeDefinition cad;  
        ......  
    } 

      由于现在所要保护的是业务方法,故我们将url变量易名为method,这样会更加明确。method变量存放类似于“sample.service.IContactManager.create”、“sample.service.IContactManager.update*”的方法名全称

    2、修改RdbmsSecuredUrlDefinition类
      将RdbmsSecuredUrlDefinition改名为RdbmsSecuredMethodDefinition,黑体部分为修改后的代码

    public class RdbmsSecuredMethodDefinition extends MappingSqlQuery{  
      
        protected static final Log log = LogFactory.getLog(RdbmsSecuredMethodDefinition.class);  
          
        public RdbmsSecuredMethodDefinition(DataSource ds) {  
            super(ds, Constants.ACEGI_RDBMS_SECURED_SQL);  
            compile();  
        }  
      
        /** 
         * convert each row of the ResultSet into an object of the result type. 
         */  
        protected Object mapRow(ResultSet rs, int rownum)  
            throws SQLException {         
            RdbmsEntryHolder rsh = new RdbmsEntryHolder();  
            // 设置业务方法  
            rsh.setMethod(rs.getString(Constants.ACEGI_RDBMS_SECURED_METHOD).trim());
              
            ConfigAttributeDefinition cad = new ConfigAttributeDefinition();  
              
            String rolesStr = rs.getString(Constants.ACEGI_RDBMS_SECURED_ROLES).trim();  
            // commaDelimitedListToStringArray:Convert a CSV list into an array of Strings  
            // 以逗号为分割符, 分割字符串  
            String[] tokens =   
                    StringUtils.commaDelimitedListToStringArray(rolesStr); // 角色名数组  
            // 构造角色集合  
            for(int i = 0; i < tokens.length;++i)  
                cad.addConfigAttribute(new SecurityConfig(tokens[i]));  
              
            //设置角色集合  
            rsh.setCad(cad);  
              
            return rsh;  
        }  
      
    }  

      其中,Constants常量类如下:

    public interface Constants {  
      
        // Acegi相关常量--------------------------------------------  
          
        // 业务方法与对应角色查询语句  
        public static final String ACEGI_RDBMS_SECURED_SQL =
         "SELECT authority,protected_res FROM authorities WHERE auth_type='FUNCTION' AND authority LIKE 'AUTH_FUNC_ContactManager%'";  
      
        // 方法字段名称  
        public static final String ACEGI_RDBMS_SECURED_METHOD = "protected_res";  
          
        // 角色字符串字段名称  
        public static final String ACEGI_RDBMS_SECURED_ROLES = "authority";  
          
    }

    3、自定义实现MethodDefinitionSource接口
      修改RdbmsFilterInvocationDefinitionSource类,改名为RdbmsMethodDefinitionSource,并修改相应方法,黑体部分为修改后的代码。

    变量:

      修改前:private RdbmsSecuredUrlDefinition rdbmsInvocationDefinition;
      修改后:private RdbmsSecuredMethodDefinition rdbmsSecuredMethodDefinition;

      以下两个函数,黑体为修改或增加部分:

    protected void initDao() throws Exception {  
        this.rdbmsSecuredMethodDefinition =   
            new RdbmsSecuredMethodDefinition(this.getDataSource()); // 传入数据源, 此数据源由Spring配置文件注入  
        ……  
    }  
      
    public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {  
        if ((object == null) || !this.supports(object.getClass())) {  
            throw new IllegalArgumentException("抱歉,目标对象不是MethodInvocation类型");  
        }  
          
        Method method = ((MethodInvocation) object).getMethod(); 
          
        List list = this.getRdbmsEntryHolderList();  
        if (list == null || list.size() == 0)  
            return null;  
          
        // 获取方法全称, 如java.util.Set.isEmpty  
        String methodString = method.getDeclaringClass().getName() + "." + method.getName();
          
        String mappedName;  
        Iterator it = list.iterator();  
        // 循环判断当前访问的方法是否设置了角色访问机制, 有则返回ConfigAttributeDefinition(角色集合), 否则返回null  
        while (it.hasNext()) {  
            RdbmsEntryHolder entryHolder = (RdbmsEntryHolder) it.next();  
            mappedName = entryHolder.getMethod();  
            boolean matched = pathMatcher.match(entryHolder.getMethod(), methodString); 
            //boolean matched = methodString.equals(mappedName) || isMatch(methodString, mappedName);  
            if (logger.isDebugEnabled()) {  
                logger.debug("匹配到如下Method: '" + methodString + ";模式为 "  
                        + entryHolder.getMethod() + ";是否被匹配:" + matched);  
            }  
      
            // 如果在用户所有被授权的URL中能找到匹配的, 则返回该ConfigAttributeDefinition(角色集合)  
            if (matched) {  
                return entryHolder.getCad();  
            }  
        }  
          
        return null;  
    } 

       4、通过Spring DI注入RdbmsMethodDefinitionSource

    <bean id="contactManagerSecurity"   
        class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">  
        <property name="authenticationManager" ref="authenticationManager" />  
        <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />  
        <property name="objectDefinitionSource" ref="<strong>rdbmsMethodDefinitionSource</strong>" />  
    </bean>  
      
    <bean id="<strong>rdbmsMethodDefinitionSource</strong>" class="sample.service.impl.<strong>RdbmsMethodDefinitionSource</strong>">  
        <property name="dataSource" ref="dataSource" />  
        <property name="webresdbCache" ref="webresCacheBackend" />  
    </bean>  
      
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>  
      
    <bean id="webresCacheBackend"   
        class="org.springframework.cache.ehcache.EhCacheFactoryBean">  
        <property name="cacheManager">  
            <ref local="cacheManager"/>  
        </property>  
        <property name="cacheName">  
            <value>webresdbCache</value>  
        </property>  
    </bean>

      至此,此节所讲的内容已结束。

  • 相关阅读:
    Html禁止粘贴 复制 剪切
    表单标签
    自构BeanHandler(用BeansUtils)
    spring配置中引入properties
    How Subcontracting Cockpit ME2ON creates SD delivery?
    cascadia code一款很好看的微软字体
    How condition value calculated in sap
    Code in SAP query
    SO Pricing not updated for partial billing items
    Javascript learning
  • 原文地址:https://www.cnblogs.com/cainiaomahua/p/8920584.html
Copyright © 2011-2022 走看看