ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ABAP权限对象值读取实战:AUTHORITY-CHECK与USRBF2封装全解

ABAP权限对象值读取实战:AUTHORITY-CHECK与USRBF2封装全解 1. 为什么要关心“权限对象的值”这件事先说个我自己的经历。前几年做一个采购审批的增强项目业务方提了个很刁钻的需求审批界面要根据当前操作人拥有的工厂权限动态决定哪些采购订单能显示、哪些按钮能点。当时第一反应就是冲着BAPI_SALESORDER_CREATEFROMDAT2这类标准BAPI的增强点去折腾做到一半才发现真正卡住的不是BAPI本身而是一个很基础的问题——怎么在ABAP代码里拿到当前用户在某权限对象上的具体值。SAP里所谓“权限对象”说白了就是一把带若干钥匙槽的锁。钥匙槽叫权限字段比如WERKS工厂、ACTVT活动类型而每个用户拥有的“钥匙齿形”就是对应字段的值比如工厂1000、活动01。系统做权限检查时拿这把钥匙去试锁能转开就放行转不开就抛权限不足的异常。这个机制人人都会配但真要写代码去“读取”这些值很多人就卡住了。常规思路是用AUTHORITY-CHECK它也不负众望地好用——前提是你只想知道“有没有权限”。但现实开发里需求往往不只是判断题更多是取数题我想知道这个用户在工厂1000上到底是能创建还是只能显示想把允许的工厂清单拉到报表里当筛选项想在一堆采购订单里先过滤出他有权限的那批再往下走业务逻辑。这些场景都需要把权限对象的值“拿出来”用而不是单纯做一次布尔判断。这篇文章就把这块内容彻底讲清楚标准检查语法怎么用、怎么不触发检查直接取授权值、批量读取所有对象怎么做、以及我在实际项目里踩过的那些坑。适合正在写增强、做权限相关报表、搞用户权限批量维护工具的ABAP开发。2. 先分清三种需求检查、判断、取值很多人在第一步就走错了方向是因为没分清“检查权限”和“读取权限值”是两回事。我把它们拆成三种场景对号入座后再选方案。2.1 只要“有没有权限”的判断题这是最典型的需求ABAP给出了标准答案——AUTHORITY-CHECK。它的设计思路很直接你把权限对象名和字段值告诉系统系统去翻用户的授权数据能匹配上就返回成功匹配不上就报错误代码。DATA: lv_werks TYPE werks_d VALUE 1000. AUTHORITY-CHECK OBJECT M_BANF_BSA ID WERKS FIELD lv_werks ID BSART FIELD NB ID ACTVT FIELD 01. IF sy-subrc 0. MESSAGE 当前用户没有工厂1000采购订单创建权限 TYPE E. ENDIF.这套语法做“能不能干”的判断非常合适因为它连底层细节都封装掉了系统内部会自己去匹配USRBF2表里的存储记录、处理通配符*、识别字段组合关系你根本不用操心这些逻辑。2.2 要“有哪些值”的取数题判断题的局限也在这——它只给你“有/没有”的结论你要的是清单。比如报表里要按用户有权限的工厂列表筛数据或者要做权限复制、权限比对工具这时候AUTHORITY-CHECK就帮不上忙了。取数题的解决方案有三个层次SAP_USER-AUTH函数、RS_SUPPORT_SELECT函数、直接读USRBF2表。三者的差异我会在后面实操章节逐个讲透这里先提个优先级判断能用函数接口尽量不要直接读表原因后面展开。2.3 要“所有权限对象”的批量导出第三种场景在权限治理项目里最常见——安全审计要导出某用户全部授权对象的完整清单或者要做权限冗余分析。这时候不仅要知道某几个对象的值还得枚举出用户拥有的所有对象再逐一提取字段值。这种需求靠一个个AUTHORITY-CHECK是不现实的需要用工具类批量处理我在第4部分会给出一个可以直接抄走的封装。2.4 顺带聊聊“SECU”这个特殊对象做权限相关开发时你可能还会碰到一个叫SECU的特殊检查对象。它管的是DP角色和PFCG角色的栏目级权限属于安全功能的基础设施。多数业务开发不用直接处理它但如果你做的是权限工具类或者角色批量维护程序建议把它在权限字段的表里过滤掉免得导出数据里混入一堆S_USR_F1、S_TCODE之类看起来像垃圾的内部对象。3. 核心细节权限对象的底层存储与关键字段要真正理解读取权限值得先看底层数据怎么躺着的。SAP用户授权关系是典型的“一对多”结构一个用户对应多个权限对象一个权限对象又由多个权限字段组合而成。这套关系在系统里靠几张底表托着最核心的就是USRBF2。3.1 USRBF2表结构与STATE状态位USRBF2的全称是“用户权限对象的自由组合表”。每个授权记录占一行核心字段包括字段说明典型值MANDT客户端300BNAME用户名DEVELOPER01AUTH权限对象名M_BANF_BSAFIELD权限字段名WERKS / BSART / ACTVTLOW字段下限值1000HIGH字段上限值空或1001STATUS状态位ACT / INACPLST权限列表版本号1这里最容易被忽略的是STATUS字段。它的含义简单说就是这一行授权是“生效”还是“不生效”。如果用户被锁定了或者角色里的某个授权被临时停用了对应行的STATUS就会变成INACinactive。直接读表不判断这个字段会把一堆实际上已经失效的权限当成有效权限导出来做出来的报表自然就不准。另一个需要注意的点是HIGH字段。并不是所有授权都是单值有些是范围授权比如工厂从1000到1999或者金额从0到99999。读表时只取LOW忽略HIGH会把范围权限漏掉一半。这也是为什么我强烈建议优先用函数而不是直接读表——函数内部把这些都处理好了。3.2 语义层面通配符*与自由组合的陷阱理解授权数据还有一个重要的语义关卡通配符*。在SAP权限模型里*不是普通字符它代表“全部”。用户有权限字段的*等价于对所有值都放行。这一下就把“直接读表”这个方案的复杂度拉高了你从USRBF2读到LOW *不能直接把它当成一个普通值往报表里塞得考虑展开成“所有工厂都允许”这种语义。AUTHORITY-CHECK能正确处理这种情况是因为系统内部把这些逻辑写死了。函数RS_SUPPORT_SELECT也是同理所以用它们就比自己处理*要省心得多。3.3 权限字段的值域ACTVT和它的兄弟们权限对象里最常遇到的字段是ACTVT活动类型它的值是两位数字编码01创建、02修改、03显示、06删除等等。不同对象里ACTVT的可用值可能不一样做权限判断时不要凭记忆写死最好先查一下TACT事务代码对应的活动文本确认当前对象到底支持哪些活动类型。除了ACTVT常见的还有WERKS工厂、EKORG采购组织、BUKRS公司代码、VKORG销售组织这类组织结构字段它们的值域基本都是主数据表里的有效编码。读取这类字段的值时最好顺带把描述也拉出来用户看到“工厂1000”比看到“1000”友好得多。4. 实操读取权限对象值的完整方案与代码从这一章开始进入实战。我会按从简到繁的顺序给出四种方案的完整实现并标注各自适用的场景。4.1 用AUTHORITY-CHECK做权限校验判断方案多数业务增强里的需求就是“判断当前用户对某工厂是否有某操作权限”这时直接用标准语法最稳。DATA: lv_werks TYPE werks_d VALUE 1000. AUTHORITY-CHECK OBJECT M_BANF_BSA ID WERKS FIELD lv_werks ID BSART FIELD NB ID ACTVT FIELD 01. CASE sy-subrc. WHEN 0. 通过继续业务逻辑 WHEN 4. 对象未分配给用户也就是没有这个对象的任何权限 WHEN 8. 对象已分配但字段值不匹配 WHEN 12. 字段值通配符*不覆盖本次检查的值 WHEN 24. 参数不完整或权限对象不存在 ENDCASE.这里源码中返回值sy-subrc的判读值得多说两句。实际项目里常见的错误是只判断 0和 0但为了排查问题更精细我会按上面对返回码分类处理。尤其是12这种“值被通配符排除”的情况和8“值不在允许列表”从业务上要分开看——前者其实是“有权限但没覆盖到这个具体值”后者才是“根本没有这个权限”。区分开以后排查效率高得多。4.2 用SAP_USER-AUTH读取授权值取数方案如果目标不是做判断而是把授权值拿出来用那就得换接口。SAP提供了两个RFC函数第一个是SAP_USER-AUTH它返回用户在某权限对象上、某字段的所有授权值。DATA: lt_values TYPE STANDARD TABLE OF string. CALL FUNCTION SAP_USER-AUTH EXPORTING object M_BANF_BSA field WERKS object_id sy-uname TABLES values lt_values. LOOP AT lt_values INTO DATA(lv_value). WRITE: / lv_value. ENDLOOP.这个函数返回的values是展开后的平铺列表通配符*会展开为*范围也会展开成多行。它最适合的场景是做下拉框候选值、报表筛选项以及“某字段允许的工厂清单”这类取数需求。有一点要注意SAP_USER-AUTH只处理单个对象的单个字段。如果你想拿一个对象下所有字段的值得写个循环或者转用下一个方案。4.3 用RS_SUPPORT_SELECT做单行读值通用取数方案如果说SAP_USER-AUTH是“给个对象字段返回所有值”那么RS_SUPPORT_SELECT就是“给个对象字段和具体值返回能否匹配”。它在内部做一次类似AUTHORITY-CHECK的匹配逻辑返回的值能直接告诉你“这个值在不在授权范围里”。DATA: lv_passed TYPE c LENGTH 1. CALL FUNCTION RS_SUPPORT_SELECT EXPORTING object M_BANF_BSA field WERKS value 1000 user sy-uname IMPORTING passed lv_passed. IF lv_passed X. WRITE: / 工厂1000有权限. ENDIF.这个函数长相很像AUTHORITY-CHECK但它不发出权限不足的异常而是安静地返回一个判断结果。它比较适合在循环里批量判断一个用户对多个工厂、多个订单的状态比如在一批PO里快速把有权限的筛出来。4.4 直接读USRBF2表批量导出方案最后一个方案也是我工作中最常用的——因为它能一次性拿到用户在所有权限对象上的全部字段和值。虽然前面提醒过直接读表的坑但在现成工具类里把这些坑都处理掉之后它就是功能最全的方案。SELECT auth, field, low, high, status FROM usrbf2 INTO TABLE DATA(lt_auth_values) WHERE bname sy-uname. SORT lt_auth_values BY auth field low. LOOP AT lt_auth_values INTO DATA(ls_auth). CHECK ls_auth-status ACT. 这里可以根据AUTH和FIELD做输出或进一步处理 ENDLOOP.这一段代码看起来简单但直接上线必踩几个坑。第一不加STATUS ACT筛选会把账号锁定前的旧授权也算进来第二不处理HIGH字段会把范围权限砍半第三不处理表里大量带*通配符的行报表里会出现一堆看起来莫名其妙的值。所以实际可用版本我会在下面给出一个封装好的工具类。4.5 一个够用的工具类实现我在项目里习惯把权限取值封装成一个工具类这样所有增强和后台任务都调同一个入口后续有权限模型变更也只需要改一处。类的核心方法是GET_AUTH_VALUES根据用户、权限对象、权限字段返回授权值内表METHOD get_auth_values. CLEAR: et_values. SELECT low, high FROM usrbf2 INTO TABLE DATA(lt_bf2) WHERE bname iv_bname AND auth iv_auth AND field iv_field AND status ACT. LOOP AT lt_bf2 INTO DATA(ls_bf2). IF ls_bf2-low *. 通配符语义为全部单独标记 APPEND VALUE #(auth iv_auth field iv_field low * high * flag ALL) TO et_values. ELSEIF ls_bf2-high IS INITIAL. 单值授权 APPEND VALUE #(auth iv_auth field iv_field low ls_bf2-low high ls_bf2-low flag SINGLE) TO et_values. ELSE. 范围授权 APPEND VALUE #(auth iv_auth field iv_field low ls_bf2-low high ls_bf2-high flag RANGE) TO et_values. ENDIF. ENDLOOP. ENDMETHOD.类里我刻意区分了三种授权形态这会直接影响上层业务逻辑。单值授权好理解范围授权在做区间判断时可以直接拿LOW/HIGH做条件通配符*则意味着顶层业务要全部放行。举个例子你要判断某个工厂在不在授权列表里如果遇到flag ALL直接返回“允许”否则再走单值和范围的匹配逻辑这才是完整准确的权限判断。5. 常见问题与排查技巧实录这块内容是我做权限类开发这几年踩坑的总结每一个问题都对应过一个真实事故。5.1 为什么读USRBF2查出来的权限和PFCG里看到的不一样这是最高频的困惑。原因其实不复杂——PFCG角色维护界面展示的是角色定义里的权限而USRBF2是用户主记录里的授权数据。两者之间隔着一层“角色分配”和“用户主记录生成”的过程。角色改了之后没有重新生成用户主记录或者用户主记录生成时失败了USRBF2里的数据就会和角色维护界面对不上。排查思路用SU01打开用户检查角色分配用事务代码SU52查看用户主记录里的授权数据如果数据缺失就执行“重新生成用户主记录”。这块操作不是ABAP开发能越俎代庖的需要和BASIS协同处理。5.2 为什么AUTHORITY-CHECK返回某个错误码但用户明明有权限最典型的场景是返回码12——值被通配符排除。用户有工厂*的权限但你在代码里写死了ID WERKS FIELD 1000系统会认为你要精确匹配1000而不是*代表的所有。这就是前面提到的语义问题*不是“匹配到具体值”而是“代表所有值”。解决方式有两个方向。方向一既然用户有了*说明业务上确实没有限制那你就应该在读取到的值为*时直接跳过该字段的检查。方向二用RS_SUPPORT_SELECT做同样的逻辑它在内部对*的处理比裸AUTHORITY-CHECK更宽松更适合做动态判断。5.3 如何判断字符串是否等于数字这个话题严格说和权限取值没有直接关系但它出现的频率极高——因为权限值里经常混着长数字编码的字段。试过用IS NUMERIC判断一个字段是不是数字但结果不稳定。后来改用COcontains only模式DATA: lv_text TYPE string VALUE 12345. IF lv_text CO 0123456789. WRITE: / 全是数字. ELSE. WRITE: / 包含非数字字符. ENDIF.注意一定要在CO后面那个常量字符串里把0到9全部写全。这个写法在ABAP新语法和旧语法里都通用不用考虑环境兼容问题。5.4 权限对象不存在或者字段拼错怎么排查AUTHORITY-CHECK返回24十有八九是三件事之一权限对象名拼错、字段名拼错、或者检查的字段组合跟权限对象定义不一致。比如M_BANF_BSA这个对象既有BSART又有WERKS和ACTVT但你只检查了其中两个字段系统照样报24。排查办法事务代码SU03查看权限对象详情或者直接查表TOBJ权限对象定义表和TOBJT权限对象文本表确认字段组合。做这种事我有个习惯写完权限检查代码后先在测试账号上故意制造一次失败确认错误码是符合预期的再继续写后面的业务逻辑。5.5 用新语法时注意权限检查不能简化ABAP 7.40以后出了很多新语法比如NEW、SWITCH、COND还有内表表达式。但AUTHORITY-CHECK这块语法没有新版本替代你没法用函数式调用去简写它只能老老实实按标准语法一行行写。有次想把AUTHORITY-CHECK封装成一个函数然后到处调用结果发现一个致命限制——权限检查必须发生在当前用户会话里在函数里调用没问题但如果你想把它封装成RFC函数然后外部系统来调权限检查的对象就不是当前登录用户了完全失去意义。这个坑值得分享给所有想写通用函数的人。6. 场景扩展权限值在增强开发里的实战用法标题里看到bapi_salesorder_createfromdat2和me55审批增强校验这两个热词说明现在做采购和销售增强的同行很多。我把权限值在这些场景下的用法补充进来你会更有体感。6.1 在BAPI创建销售订单前做权限预判BAPI_SALESORDER_CREATEFROMDAT2本身不强制做权限检查但如果你的客户要求“创建订单前先校验当前用户对销售组织、分销渠道是否有创建权限”你不能把所有逻辑都压到BAPI内部报错上。更优雅的做法是在调用BAPI之前拉一次用户权限提前把没有权限的组织组合过滤掉。实操时我一般这样处理先从输入参数里取销售组织VKORG、分销渠道VTWEG、产品组SPART然后调用工具类的GET_AUTH_VALUES拿当前用户对V_VBA_AAG这个权限对象销售订单创建对象的VKORG、VTWEG、SPART值再做一次逐项匹配。不通过的就返回友好提示消息而不是等BAPI抛一堆E类型错误。6.2 在ME55审批增强里叠加业务权限校验采购审批的增强需求比销售更繁琐因为审批流程里不只是“有没有权限”的问题还有“这单该不该他审批”的业务规则。常见的做法是在ME55的 BADI 或出口里加一层增强校验先取当前用户对采购组织EKORG、工厂WERKS的权限值再和待审批采购订单的组织数据进行比对。比较典型的场景是采购订单属于工厂1000但审批人只有工厂2000的审批权限那这单就不该出现在他的审批队列里。这种判断如果用AUTHORITY-CHECK做没问题但如果你要顺便把“允许审批的工厂清单”展示到审批界面让用户知道范围那就必须用到读取权限值的方案了。6.3 权限值驱动的动态界面控制还有一类更灵活的应用界面按钮和字段的可见性由权限值直接驱动。比如某个用户没有某工厂的创建权限那界面上“创建”按钮就该置灰而不是点了才报错。这种前置判断对用户体验的改善非常明显而且对权限模型的利用也更深入——不再只是“有没有权限”而是“有什么权限”基于权限内容做界面适配。我把它称为“权限感知式界面设计”理念很直白把权限数据当成界面配置的输入源而不是事后校验器。在Fiori和应用里这一招尤其好用因为前端可以预先拿到权限清单动态渲染按钮和列。7. 权限值读取的性能与安全注意事项最后说几个在性能和合规层面的细节。权限表本身不大但如果你在高频调用路径里反复读USRBF2还是有性能隐患的。每次调用都从头扫描整张用户权限表在高并发的批处理里会把数据库拖慢。我的经验是分层缓存会话级缓存一次读取结果用SORT和二分查找代替线性循环大量数据用FOR ALL ENTRIES IN或者IN操作符批量取。如果只能拿到用户名列表需要批量为多用户取权限值顺序循环RS_SUPPORT_SELECT是不行的要用PARALLEL或者直接对USRBF2做一次SELECT实现批量读取。安全方面要格外谨慎——权限数据属于敏感数据。做了权限查询报表之后一定要加上AUTHORITY-CHECK OBJECT S_DEVELOP这类管理员权限检查避免把普通用户的权限结构暴露给其他用户。我在第一版权限导出工具里就没注意这点结果被安全审计打回重做这个教训不用再踩第二遍。另外USRBF2表数据结构的稳定性是SAP内部授权机制保证的但因为业务改动或者升级导致字段语义变化这类问题在项目里发生过。关注你项目里的授权对象维护情况尤其要注意对象是否启用了新的字段组合比如某个对象原来只有WERKS后来加上了EKORG那老代码里只查WERKS的权限判断就会漏掉新的维度。这种问题不会报错表现出来就是“明明配置了权限还是没有权限”或者“权限判断结果总是不对”排查起来极费劲。用权限值做判断时还有一个容易忽略的细节别把权限字段的值当主数据的值去用。权限值有可能是旧编码、失效编码甚至是*通配符这些进报表之前都要做清洗。我记得有次在报表里直接拿权限值去关联工厂表结果工厂表里根本找不到*这个值关联出来全是空记录。从那以后我就在工具类里把通配符和范围值都单独标出来不让它们混入正常值参与关联。8. 实用小技巧收集挑几个开发里高频用到的小技巧每个都不长但能省不少事。判断权限对象是否存在可以用TOBJ表SELECT SINGLE object FROM tobj INTO DATA(lv_obj) WHERE object M_BANF_BSA.拉取权限字段文本描述可以用TOBJT和TDW4关联这样导出报表时能显示出“工厂”“活动”而不是干巴巴的字段名。判断当前用户是否有某个角色直接查AGR_USERS表就行比遍历授权数据快得多。SELECT COUNT(*) FROM agr_users WHERE uname sy-uname AND agr_name Z_DEVELOPER.在权限检查时输出具体的字段值可以在检查前把ID WERKS FIELD lv_werks里lv_werks打印到日志里方便操作人员定位到底卡在哪个字段上。这个做法不需要改任何配置纯代码层面做轮询调试实测比在线上反复用SU53查错误归因高效得多。这些技巧看起来零散但组合起来用能把权限相关开发的整体效率提升不少。特别是那些需要反复比对权限配置和实际数据是否一致的项目把查询接口和日志点都铺好后面排查问题能省下一大半时间。我现在做一个新权限需求时固定在开发机上先写好三件套一个能跑通的AUTHORITY-CHECK样本、一个能输出授权值清单的调试报表、一套完整的USRBF2查询封装然后才开始写正式的逻辑代码。这样从项目一开始就把调试路径铺好后面无论是写代码还是应付审计都有底气得多。
返回列表