???????

2012-01-31 16:26:43by bobbie.zou

框架级参数验证

目的

框架级防止用户任意输入url参数,查看别人单据,或其他不该自己看到信息

示例

工作流实例检查,看当前用户是否在该实例的审批列表以及通知列表里面

  1. 1.在../WEB-INF/aurora.feature/service-listener.config中添加<participant class="aurora.security.AccessCheck"/>

    <participant-manager xmlns="uncertain.event">
    	<participant-list category="service">
    		...
    		<participant class="aurora.security.AccessCheck"/>
    	</participant-list>	
    </participant-manager>
                	
  2. 2.在../WEB-INF/aurora.feature/service-procedure.config中<p:procedure name="pre-service">节点末尾添加<p:action Name="AccessCheck" />

    <?xml version="1.0" encoding="UTF-8"?>
    <p:procedure-registry xmlns:t="aurora.application.action" xmlns:a="http://www.aurora-framework.org/application"
     xmlns:p="uncertain.proc">
        <p:procedures>
            <p:procedure name="pre-service">
                <p:set field="@success" value="true"/>        
                <!--                  
                <a:ntlm-login/>                     
                -->
                <t:session-copy/>            
                <p:switch test="@is_autocrud_service">
                    <p:case Value="true">
                        <t:bm-access-check errorMessage="没有权限访问,或登录已失效"/>
                    </p:case>
                    <p:case>
                        <t:resource-access-check resultPath="/access-check/@status_code" 
    errorMessage="登录已失效,请重新登录"/>
                    </p:case>
                </p:switch>
                <t:check-dispatch dispatchUrl="${/request/@context_path}/error_screen_unregistered.screen" 
    field="/access-check/@status_code" message="页面没有注册" value="unregistered"/>
                <t:check-dispatch dispatchUrl="${/request/@context_path}/error_screen_unauthorized.screen" 
    field="/access-check/@status_code" message="没有权限访问指定的页面" value="unauthorized"/>
                <t:check-dispatch dispatchUrl="${/request/@context_path}/error_session_expired.screen" 
    field="/access-check/@status_code" message="登录已失效,请重新登录" value="login_required"/>            
                <!-- 框架级参数校验 -->
                <p:action Name="AccessCheck" />
            </p:procedure>
            <p:procedure name="application-start">
                <p:invoke procedure="init.load_system_service"/>
                <p:invoke procedure="init.load_priviledge_check_data"/>
            </p:procedure>
            <p:procedure name="session-destroy">
                <a:model-update model="sys.sys_expire_session"/>
            </p:procedure>
        </p:procedures>
    </p:procedure-registry>
                	
  3. 3.通过配置文件../WEB-INF/aurora.security/access-check-rule.config,集中配置所有的参数校验规则:

    <?xml version="1.0" encoding="UTF-8"?>
    <s:access-check-rule-config xmlns:s="aurora.security">
      <access-check-rules>
        <s:access-check-rule name="wflInstanceCheck" description="工作流实例检查,看当前用户是否在该实例的审批列表以及通知列表里面" 
    checkBM="security.wfl_instance_permission_check" checkField="@IS_VALID" successValue="Y" />
        <s:access-check-rule name="financeCheck" description="财务人员查看权限"checkBM="security.finance_permission_check" 
    checkField="@IS_VALID" successValue="Y"/>
      </access-check-rules>	
    </s:access-check-rule-config>
    				

    Table 1. access-check-rule标记属性

    属性描述
    name验证规则名称
    description验证规则描述
    checkBM指定的验证BM
    checkField校验BM返回的结果
    successValue验证成功参考值,和否和checkField一致

  4. 4.定义验证BM(如security.wfl_instance_permission_check):

    <bm:model xmlns:bm="http://www.aurora-framework.org/schema/bm">
        <bm:operations>
            <bm:operation name="query">
                <bm:query-sql>           
                select 'Y' as IS_VALID from dual
    	            where exists (
    		          select r.instance_id from wfl_notification_record r 
                                where r.instance_id = ${/parameter/@instance_id} and r.user_id = ${/session/@user_id}
    		    union all 
    			  select t.instance_id from wfl_instance_node_recipient t 
                                where t.instance_id = ${/parameter/@instance_id} and t.user_id = ${/session/@user_id}
    				)
                </bm:query-sql>
            </bm:operation>
        </bm:operations>
        <bm:fields>
            <bm:field name="IS_VALID"/>
        </bm:fields>
    </bm:model>				
    				
  5. 5.在所有需要验证的screen/svc中,设置tag,说明要进行哪些检查:

    <?xml version="1.0" encoding="UTF-8"?>
    <screen>
    	<access-check name="wflInstanceCheck" />
    	<init-procedure>
    	...
    	</init-procedure>
    	<view>
    	...
    	</view>
    </screen>
    				

特别说明

  1. 1、关于工作流审批嵌入界面

    对于工作流审批界面直接嵌入的screen,因为是采用screen-include方式,所以没有安全性问题。嵌入的单据界面,参数是在server端解析传递的,不会被客户端篡改。整个安全性验证是以工作流的待审批记录id为依据,只要客户端传递的审批记录id合法就OK。但是,要防止用户获得工作流嵌入的显示页面名称,直接敲地址参数访问。一般只要这类界面对应的功能没有直接被分配给任何角色就好了。

    But,如果审批界面中还要点个链接,弹出一个新页面,这个新页面就要做参数验证了。比如,报销单审批界面中,点按钮弹出分配行明细界面,这个界面有可能被用户看到url和参数,所以就要做参数验证。因为工作流可能会被配置成各种可能的用户参与审批,所以直接应用上面那种业务规则往往不行。比如,张三审批李四的报销单,张三既不是分公司财务,也不是总公司财务,但对于这一单来说,他有权限查看。

    这时候,可以考虑写一个专为审批查看使用的screen,参数为工作流instance_id+报销单行id,验证规则为当前用户有权限审批参数指定的工作流记录。然后,通过sql查出合法的line_id记录,例如 select line_id from exp_lines where line_id=${/parameter/@line_id} and head_id = 根据工作流instance_id获取单据头id。如果客户伪造一个不该他审批的line_id作为参数传递,上面这段sql不会有记录。最后,用 <screen-include screen="查看分配行.screen?line_id=${/model/check-record/@line_id}">包含一个现成的分配行查看界面。这样可以绕过分配行查看原来的业务规则(因为参数验证是以screen/svc为单位进行,不会对screen-include包含的界面再进一步检查),重用现有界面,又能保证安全性。

  2. 2、什么时候用参数验证,什么时候用BM的data-filter

    BM的data-filter可以施加一些强制性的where限制,也可以起到类似的效果。如果所有查看页面所需要的数据,其来源BM都通过派生获得了这种安全性where条件限制,那么就能保证传入非法参数时查不出记录,这时候直接用BM的data-filter就可以了。

    如果当初设计BM的时候没有考虑周全,或者screen/svc里面不是每一个BM操作对应的BM都有这种安全性防范,或者验证逻辑很复杂,不适合直接写成where表达式,那就考虑用access-check特性。

Demo
    Attachments

      Comments

      0 Responses to the article

      暂时没有评论。

      发表评论