SAP 权限控制深度解析:自定义程序与标准事务码的边界与协同
关键词 :ABAP 权限 / AUTHORITY-CHECK / PFCG / Q_INSPTYPE / S_TCODE / SU24 / SU53
适用版本:SAP NetWeaver 7.x / S/4HANA 通用
一、问题的缘起
在 QM 检验批自建程序的开发中,一个典型场景经常引发讨论:为自定义 Z 程序添加 AUTHORITY-CHECK 控制"能看哪些工厂、哪些检验类型"之后,很多开发者会产生一个疑问------这道校验会不会影响用户在标准事务码 QA32 中看到的数据范围?
这个问题看似简单,实则牵出了 SAP 权限控制体系的完整认知框架:共同点、分水岭、代码陷阱、标准 vs 自定义的边界、以及最隐蔽的"绕过"风险。本文试图把这些问题系统化梳理清楚,给出一份可落地的权限设计参考。
二、核心结论
结论一:不会影响。
自定义 Z 程序中的 AUTHORITY-CHECK,对标准事务码(如 QA32)零影响。两套程序各自在运行时做自己的权限检查,修改 A 程序的代码永远改变不了 B 程序的行为。
结论二:但可以做到「一份配置两头生效」。
如果 Z 程序使用标准授权对象 Q_INSPTYPE(工厂+检验类型),而 QA32 工作清单原生也按 Q_INSPTYPE 过滤------那么只要 PFCG 给用户配一份 Q_INSPTYPE,QA32 和 Z 程序会自动同时受控,无需写两套权限。
#mermaid-svg-rudZWFBbffEsN0Yp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rudZWFBbffEsN0Yp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rudZWFBbffEsN0Yp .error-icon{fill:#552222;}#mermaid-svg-rudZWFBbffEsN0Yp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rudZWFBbffEsN0Yp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rudZWFBbffEsN0Yp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rudZWFBbffEsN0Yp .marker.cross{stroke:#333333;}#mermaid-svg-rudZWFBbffEsN0Yp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rudZWFBbffEsN0Yp p{margin:0;}#mermaid-svg-rudZWFBbffEsN0Yp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rudZWFBbffEsN0Yp .cluster-label text{fill:#333;}#mermaid-svg-rudZWFBbffEsN0Yp .cluster-label span{color:#333;}#mermaid-svg-rudZWFBbffEsN0Yp .cluster-label span p{background-color:transparent;}#mermaid-svg-rudZWFBbffEsN0Yp .label text,#mermaid-svg-rudZWFBbffEsN0Yp span{fill:#333;color:#333;}#mermaid-svg-rudZWFBbffEsN0Yp .node rect,#mermaid-svg-rudZWFBbffEsN0Yp .node circle,#mermaid-svg-rudZWFBbffEsN0Yp .node ellipse,#mermaid-svg-rudZWFBbffEsN0Yp .node polygon,#mermaid-svg-rudZWFBbffEsN0Yp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rudZWFBbffEsN0Yp .rough-node .label text,#mermaid-svg-rudZWFBbffEsN0Yp .node .label text,#mermaid-svg-rudZWFBbffEsN0Yp .image-shape .label,#mermaid-svg-rudZWFBbffEsN0Yp .icon-shape .label{text-anchor:middle;}#mermaid-svg-rudZWFBbffEsN0Yp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rudZWFBbffEsN0Yp .rough-node .label,#mermaid-svg-rudZWFBbffEsN0Yp .node .label,#mermaid-svg-rudZWFBbffEsN0Yp .image-shape .label,#mermaid-svg-rudZWFBbffEsN0Yp .icon-shape .label{text-align:center;}#mermaid-svg-rudZWFBbffEsN0Yp .node.clickable{cursor:pointer;}#mermaid-svg-rudZWFBbffEsN0Yp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rudZWFBbffEsN0Yp .arrowheadPath{fill:#333333;}#mermaid-svg-rudZWFBbffEsN0Yp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rudZWFBbffEsN0Yp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rudZWFBbffEsN0Yp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rudZWFBbffEsN0Yp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rudZWFBbffEsN0Yp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rudZWFBbffEsN0Yp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rudZWFBbffEsN0Yp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rudZWFBbffEsN0Yp .cluster text{fill:#333;}#mermaid-svg-rudZWFBbffEsN0Yp .cluster span{color:#333;}#mermaid-svg-rudZWFBbffEsN0Yp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rudZWFBbffEsN0Yp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rudZWFBbffEsN0Yp rect.text{fill:none;stroke-width:0;}#mermaid-svg-rudZWFBbffEsN0Yp .icon-shape,#mermaid-svg-rudZWFBbffEsN0Yp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rudZWFBbffEsN0Yp .icon-shape p,#mermaid-svg-rudZWFBbffEsN0Yp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rudZWFBbffEsN0Yp .icon-shape .label rect,#mermaid-svg-rudZWFBbffEsN0Yp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rudZWFBbffEsN0Yp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rudZWFBbffEsN0Yp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rudZWFBbffEsN0Yp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} PFCG 一份授权
Q_INSPTYPE
标准事务码 QA32
原生按 Q_INSPTYPE 过滤
自定义 Z 程序
显式 AUTHORITY-CHECK
✅ 用户权限一致
三、共同点:底层是一家
无论标准程序还是自定义程序,权限控制都共用同一套 SAP 授权基础设施,本质都是 AUTHORITY-CHECK 在用户授权缓冲(SU56)里查授权对象值。
| 维度 | 说明 |
|---|---|
| 授权对象 | 都来自 SU21 定义的授权对象(标准对象如 Q_INSPTYPE、S_TCODE,或自建 Z* 对象) |
| 角色分配 | 都靠 PFCG 把授权值填进角色、分配给用户 |
| 生命周期 | 都要走 生成参数文件 → 用户比较(RHAUTUPD_NEW / PFUD)→ 用户重登 三步,缺一步就"配了像没配" |
| 判定机制 | 都靠 AUTHORITY-CHECK 返回 sy-subrc:0=通过,4/12/24=拒绝 |
| 排查手段 | 失败都可用 /NSU53 抓当前缺哪个对象 |
从机制角度看,两者不是两套体系,而是同一套机制的两种触发方式。
四、分水岭:七个维度的差异
标准程序与自定义程序真正的差异,可以从七个维度刻画:
| # | 区别点 | 标准程序 | 自定义程序 |
|---|---|---|---|
| 1 | 谁来写检查 | SAP 已把 AUTHORITY-CHECK 写进代码,自动触发 |
必须自己写,不写 = 完全不检查 |
| 2 | SELECT 直读 | 通用表工具(SE16/SM30)自动查 S_TABU_DIS |
用 SELECT 直读不触发 S_TABU_DIS |
| 3 | 事务码保护 | 标准 T-code 自动查 S_TCODE |
经 SE38/SA38 跑查 S_PROGRAM/S_DEVELOP;须用 SE93 包成 Z 事务码才走 S_TCODE |
| 4 | 缺对象的处理 | SAP 对 sy-subrc <> 0(含 12/24)一律拒绝 |
新手常只判 = 4,漏掉 12 → 校验形同虚设 |
| 5 | 可控维度 | SAP 设计好了(如 QA32 只能按 工厂+检验类型) | 可查任意字段,灵活但责任全在开发者 |
| 6 | 数据级(行级)安全 | BW 分析授权(RSECADMIN)标准查询自动执行 | 自定义直读 BW 须显式调 RSDRI 等并带授权参数 |
| 7 | 检查建议值(SU24) | SAP 已维护,PFCG 建角色时自动带出 | 自定义事务码需自己 在 SU24 维护,否则 Basis 无从下手 |
用一句话概括:标准程序是"出厂自带锁",自定义程序是"你爱加不加的锁"------不加就门户大开。
五、代码实践:两种典型写法
以"按工厂+检验类型控制检验批建批权限"为例(标准对象 Q_INSPTYPE)。
5.1 模式一:过滤输出(小数据量)
abap
SELECT * FROM qals INTO TABLE @lt_qals WHERE ...
LOOP AT lt_qals ASSIGNING <fs_qals>.
AUTHORITY-CHECK OBJECT 'Q_INSPTYPE'
ID 'WERKS' FIELD <fs_qals>-werk
ID 'QPRUEFART' FIELD <fs_qals>-art.
IF sy-subrc <> 0.
DELETE lt_qals. " 无权限行剔除
ENDIF.
ENDLOOP.
5.2 模式二:DB 层 RANGE 限制(大数据量必用)
abap
" 先把用户有权限的工厂/检验类型构建成 RANGE,加到 WHERE
SELECT werks, qpruefart FROM agr_xxx ... INTO ... " 按用户授权展开
SELECT * FROM qals INTO TABLE @lt_qals
WHERE werks IN @lr_werks
AND art IN @lr_art.
5.3 字段名陷阱
检验类型在表 QALS/屏幕里叫 ART ,但在授权对象里字段名是 QPRUEFART 。写 ID 'ART' 校验会永远失效 ,必须用 QPRUEFART。
5.4 sy-subrc 铁律
abap
AUTHORITY-CHECK OBJECT 'Q_INSPTYPE'
ID 'WERKS' FIELD in_qals_imp-werk
ID 'QPRUEFART' FIELD in_qals_imp-art.
IF sy-subrc <> 0. " 只用 <> 0 拦截,别只写 = 4
" 4 = 无此授权值
" 12 = 用户根本没有该对象
" 24 = 用户 profile 里未包含
msgty = 'E'.
msgtx = '当前用户无该工厂/检验类型建批权限'.
RETURN.
ENDIF.
漏判 12/24 是自定义程序最常见的安全漏洞 ------用户根本没有 Q_INSPTYPE 时,sy-subrc = 12,若只判 = 4,校验会直接放行。
#mermaid-svg-kumWZkOIRH262bJS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kumWZkOIRH262bJS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kumWZkOIRH262bJS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kumWZkOIRH262bJS .error-icon{fill:#552222;}#mermaid-svg-kumWZkOIRH262bJS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kumWZkOIRH262bJS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kumWZkOIRH262bJS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kumWZkOIRH262bJS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kumWZkOIRH262bJS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kumWZkOIRH262bJS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kumWZkOIRH262bJS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kumWZkOIRH262bJS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kumWZkOIRH262bJS .marker.cross{stroke:#333333;}#mermaid-svg-kumWZkOIRH262bJS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kumWZkOIRH262bJS p{margin:0;}#mermaid-svg-kumWZkOIRH262bJS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kumWZkOIRH262bJS .cluster-label text{fill:#333;}#mermaid-svg-kumWZkOIRH262bJS .cluster-label span{color:#333;}#mermaid-svg-kumWZkOIRH262bJS .cluster-label span p{background-color:transparent;}#mermaid-svg-kumWZkOIRH262bJS .label text,#mermaid-svg-kumWZkOIRH262bJS span{fill:#333;color:#333;}#mermaid-svg-kumWZkOIRH262bJS .node rect,#mermaid-svg-kumWZkOIRH262bJS .node circle,#mermaid-svg-kumWZkOIRH262bJS .node ellipse,#mermaid-svg-kumWZkOIRH262bJS .node polygon,#mermaid-svg-kumWZkOIRH262bJS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kumWZkOIRH262bJS .rough-node .label text,#mermaid-svg-kumWZkOIRH262bJS .node .label text,#mermaid-svg-kumWZkOIRH262bJS .image-shape .label,#mermaid-svg-kumWZkOIRH262bJS .icon-shape .label{text-anchor:middle;}#mermaid-svg-kumWZkOIRH262bJS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kumWZkOIRH262bJS .rough-node .label,#mermaid-svg-kumWZkOIRH262bJS .node .label,#mermaid-svg-kumWZkOIRH262bJS .image-shape .label,#mermaid-svg-kumWZkOIRH262bJS .icon-shape .label{text-align:center;}#mermaid-svg-kumWZkOIRH262bJS .node.clickable{cursor:pointer;}#mermaid-svg-kumWZkOIRH262bJS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kumWZkOIRH262bJS .arrowheadPath{fill:#333333;}#mermaid-svg-kumWZkOIRH262bJS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kumWZkOIRH262bJS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kumWZkOIRH262bJS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kumWZkOIRH262bJS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kumWZkOIRH262bJS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kumWZkOIRH262bJS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kumWZkOIRH262bJS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kumWZkOIRH262bJS .cluster text{fill:#333;}#mermaid-svg-kumWZkOIRH262bJS .cluster span{color:#333;}#mermaid-svg-kumWZkOIRH262bJS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kumWZkOIRH262bJS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kumWZkOIRH262bJS rect.text{fill:none;stroke-width:0;}#mermaid-svg-kumWZkOIRH262bJS .icon-shape,#mermaid-svg-kumWZkOIRH262bJS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kumWZkOIRH262bJS .icon-shape p,#mermaid-svg-kumWZkOIRH262bJS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kumWZkOIRH262bJS .icon-shape .label rect,#mermaid-svg-kumWZkOIRH262bJS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kumWZkOIRH262bJS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kumWZkOIRH262bJS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kumWZkOIRH262bJS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0
4
12
24
AUTHORITY-CHECK 返回 sy-subrc
sy-subrc 值
✅ 通过
❌ 无此授权值
❌ 用户根本没该对象
❌ Profile 中未包含
正确做法:IF sy-subrc <> 0
全部拒绝,安全
六、QA32 的权限控制真相
用 /NSU53 可在打开 QA32 失败后看到实际缺哪个对象:
| 对象 | 字段 | 控制内容 |
|---|---|---|
| Q_TCODE | TCD = QA32 | 能不能进 QA32 |
| Q_INSPTYPE | WERKS + QPRUEFART | 按工厂能看哪些检验类型的批(控制"工厂维度"的标准手段) |
| Q_QM_ALL | QM_WERKS + ACTVT | QM 通用授权(按工厂) |
| Q_MATERIAL | 物料授权组 | 按物料授权组(需 MM 主数据 QM 视图维护授权组) |
标准行为 :用户没有包含在 Q_INSPTYPE 里的工厂/检验类型,其检验批在 QA32 工作清单里会被过滤掉(看不到)。
一个常见的误解 是"QA32 没法做权限,权限都做在它跳转的事务码(QA11/QA02/QE51)上"。这只对一半------QA32 确实是网关,点进去的具体动作各有独立授权检查;但 QA32 工作清单本身确实按 Q_INSPTYPE 过滤,"在 QA32 看不到某工厂的批"是标准能实现的。
标准做不到的情况 :想按物料类型/物料组/工作中心限制 QA32 看到的批,Q_INSPTYPE 不够。可选方案:
- 用户出口 / BAdI 增强
- 用
Q_MATERIAL(物料授权组)+ 物料主数据维护授权组 - 按工作中心区分
七、深入理解「一份配置两头生效」
这是本文标题中最容易被忽略但最重要的一点,值得单独展开。
7.1 门禁卡类比
想象公司有两道门:
| 门 | 谁装的 | 读卡器型号 |
|---|---|---|
| A 门(标准程序 QA32) | 物业装的 | 读 Q_INSPTYPE 卡 |
| B 门(Z 程序) | 自己装的 | 也选 Q_INSPTYPE 读卡器 |
因为 B 门选的读卡器型号和 A 门一样,所以:
- 人事部在系统里给员工张三办一张
Q_INSPTYPE卡 - 张三同时能刷开 A 门和 B 门
- 不需要为 B 门单独办第二张卡
如果 B 门选了另一个型号的读卡器 (比如自建的 Z_INSPTYPE),则需要:
- 为张三办两张卡
- 每次员工入职/调岗/离职,都要同步维护两套
- 忘了维护任何一套,就出现"A 门能进、B 门进不去"或反过来的割裂
7.2 具体对比
假设公司有 3 个工厂:1000、2000、3000。用户张三只能看工厂 1000 的检验批。
✅ 好做法 :PFCG 里配一次 Q_INSPTYPE,QA32 和 Z 程序同时生效。
❌ 坏做法 :Z 程序用自建 Z_INSPTYPE,PFCG 要配两次,容易割裂。
| 对比项 | 用标准对象(Q_INSPTYPE) | 用自建对象(Z_INSPTYPE) |
|---|---|---|
| PFCG 配置次数 | 1 次 | 2 次 |
| 两边权限是否一致 | 自动一致 | 可能不一致 |
| 员工调岗维护 | 改 1 处 | 改 2 处 |
| Basis 沟通成本 | 低(SU24 已有建议值) | 高(要额外维护 SU24) |
前提:标准授权对象能覆盖业务需求。当它管不了物料类型/工作中心这类维度时,才考虑自建对象或增强方案。
八、延伸场景:只配 Z 事务码、不配 QA32 安全吗?
这是一个非常实际的权限设计场景。答案需要从两层权限的角度来看。
8.1 结论先行
| 问题 | 答案 |
|---|---|
| 用户能不能进 QA32? | ❌ 不能(S_TCODE 拦截) |
| 用户能不能用自建 Z 程序? | ✅ 能(S_TCODE 配了 Z 事务码) |
| Z 程序能看到的数据 = QA32 能看到的数据吗? | ⚠️ 不一定,取决于 Z 程序检查了什么数据权限对象 |
| 这算不算"绕过 QA32"? | 🚨 可能是安全漏洞,视场景而定 |
8.2 两层权限的分工
#mermaid-svg-Gra4S2tAMDKDk7gL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Gra4S2tAMDKDk7gL .error-icon{fill:#552222;}#mermaid-svg-Gra4S2tAMDKDk7gL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Gra4S2tAMDKDk7gL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Gra4S2tAMDKDk7gL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Gra4S2tAMDKDk7gL .marker.cross{stroke:#333333;}#mermaid-svg-Gra4S2tAMDKDk7gL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Gra4S2tAMDKDk7gL p{margin:0;}#mermaid-svg-Gra4S2tAMDKDk7gL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster-label text{fill:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster-label span{color:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster-label span p{background-color:transparent;}#mermaid-svg-Gra4S2tAMDKDk7gL .label text,#mermaid-svg-Gra4S2tAMDKDk7gL span{fill:#333;color:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL .node rect,#mermaid-svg-Gra4S2tAMDKDk7gL .node circle,#mermaid-svg-Gra4S2tAMDKDk7gL .node ellipse,#mermaid-svg-Gra4S2tAMDKDk7gL .node polygon,#mermaid-svg-Gra4S2tAMDKDk7gL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Gra4S2tAMDKDk7gL .rough-node .label text,#mermaid-svg-Gra4S2tAMDKDk7gL .node .label text,#mermaid-svg-Gra4S2tAMDKDk7gL .image-shape .label,#mermaid-svg-Gra4S2tAMDKDk7gL .icon-shape .label{text-anchor:middle;}#mermaid-svg-Gra4S2tAMDKDk7gL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Gra4S2tAMDKDk7gL .rough-node .label,#mermaid-svg-Gra4S2tAMDKDk7gL .node .label,#mermaid-svg-Gra4S2tAMDKDk7gL .image-shape .label,#mermaid-svg-Gra4S2tAMDKDk7gL .icon-shape .label{text-align:center;}#mermaid-svg-Gra4S2tAMDKDk7gL .node.clickable{cursor:pointer;}#mermaid-svg-Gra4S2tAMDKDk7gL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Gra4S2tAMDKDk7gL .arrowheadPath{fill:#333333;}#mermaid-svg-Gra4S2tAMDKDk7gL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Gra4S2tAMDKDk7gL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Gra4S2tAMDKDk7gL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Gra4S2tAMDKDk7gL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Gra4S2tAMDKDk7gL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Gra4S2tAMDKDk7gL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster text{fill:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL .cluster span{color:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Gra4S2tAMDKDk7gL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Gra4S2tAMDKDk7gL rect.text{fill:none;stroke-width:0;}#mermaid-svg-Gra4S2tAMDKDk7gL .icon-shape,#mermaid-svg-Gra4S2tAMDKDk7gL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Gra4S2tAMDKDk7gL .icon-shape p,#mermaid-svg-Gra4S2tAMDKDk7gL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Gra4S2tAMDKDk7gL .icon-shape .label rect,#mermaid-svg-Gra4S2tAMDKDk7gL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Gra4S2tAMDKDk7gL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Gra4S2tAMDKDk7gL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Gra4S2tAMDKDk7gL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 权限控制两层
第一层:S_TCODE
能不能启动这个事务码
第二层:数据权限对象
如 Q_INSPTYPE
进去后能看到哪些数据
QA32 用 QA32
Z 程序用 ZQM_REPORT
各自独立
两边都用 Q_INSPTYPE
→ 数据权限一致 ✅
Z 程序用自建对象
→ 数据权限可能不一致 🚨
关键点 :S_TCODE 只管"能不能进去",Q_INSPTYPE 管"进去后能看到哪些数据",两者互不干涉。
8.3 两种典型情况
情况 A:Z 程序用标准对象 Q_INSPTYPE(安全)
S_TCODE:
✅ ZQM_REPORT
❌ QA32(故意不给)
数据权限:
✅ Q_INSPTYPE WERKS = 1000, QPRUEFART = 01,02
- 用户进不了 QA32 ✅
- 用户用 Z 程序,只看到工厂 1000 的数据 ✅
- 数据权限由
Q_INSPTYPE统一控制 ✅
情况 B:Z 程序用自建对象且全放开(危险)
S_TCODE:
✅ ZQM_REPORT
❌ QA32
数据权限:
✅ Z_QM_ALL ALL = 'X' ← 自建对象,全放开
- 用户进不了 QA32(假象)✅
- 用户通过 Z 程序看到了工厂 2000 的全量数据 🚨
- 权限设计被绕过 🚨
- 审计/合规风险 🚨
8.4 判断标准
| 判断维度 | 安全 | 危险 |
|---|---|---|
| Z 程序用什么数据对象? | 标准对象(Q_INSPTYPE) | 自建对象(Z_*)或不用 |
| 过滤逻辑和 QA32 一致吗? | 一致 | 不一致或更宽松 |
| 用户能看到的数据范围 | ⊆ QA32 的范围 | ⊃ QA32 的范围 |
| 是否经过安全审核 | 是 | 否 |
一句话:Z 程序的数据权限范围 ≤ QA32 的范围 → 安全;反之 → 安全漏洞。
8.5 别忽略"其他入口"
即使不给用户 QA32 的 S_TCODE,用户仍可能通过其他入口访问 QA32 数据:
| 潜在入口 | 前提条件 | 风险 |
|---|---|---|
| SE38 → 运行标准程序 | 用户有 S_DEVELOP |
高 |
| SE16/SE16N → 直接查 QALS | 用户有 S_TABU_DIS 的 QALS 授权组 |
高 |
| 从其他事务码跳转 | S_TCODE 依然会检查 |
中 |
| 用 SM30 维护视图 | 用户有 S_TABU_DIS |
中 |
| 通过 BI/报表工具 | 取决于工具权限 | 中 |
因此,光不给 S_TCODE 是不够的,还要在表级用 S_TABU_DIS(授权组)防护。
九、避坑清单
□ 1. 只用 IF sy-subrc <> 0 拦截,别只写 = 4。漏 12/24 等于权限白配。
□ 2. 大数据量用"授权 RANGE + WHERE",别 SELECT 后逐行 AUTHORITY-CHECK + DELETE。
后者会把无权限行读进内表(数据泄露窗口)且性能差。
□ 3. 选择屏幕校验(AT SELECTION-SCREEN)不能单独用------用户全选 * 就绕过,
只能作为友好提示,真正拦截面在取数/处理时。
□ 4. 两层防护:代码里 AUTHORITY-CHECK(控业务维度)+ 表上 S_TABU_DIS
(防用户绕过程序去 SE16 看整张表)。
□ 5. 对齐标准、复用同一对象:自定义建批程序用 Q_INSPTYPE,QA32 也认它
→ 一份 PFCG 授权两端同时生效。
□ 6. 自定义事务码记得去 SU24 维护检查建议值,让 Basis 建角色时自动带出。
□ 7. 配完三步别漏:生成 → 用户比较 → 重登;用 /NSU53 验证、SU56 查缓冲。
□ 8. 行级/数据级安全别想当然:BW 分析授权自定义程序直读不过滤,
必须显式调用带授权的 API。
□ 9. 只配 Z 事务码、不配 QA32 → Z 程序必须用标准对象 Q_INSPTYPE 检查,
否则就是绕过标准权限的安全漏洞。
□ 10. 字段名陷阱:检验类型在表里叫 ART,在授权对象里叫 QPRUEFART。
十、总结
机制同源、对象通用、生命周期相同------这是自定义与标准权限控制的共同点。
真正的分水岭是"谁触发检查":
- 标准:由 SAP 写死自动执行
- 自定义 :由开发者显式写
AUTHORITY-CHECK,不写就不查
#mermaid-svg-vKHL0gDEdg1CRM0h{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vKHL0gDEdg1CRM0h .error-icon{fill:#552222;}#mermaid-svg-vKHL0gDEdg1CRM0h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vKHL0gDEdg1CRM0h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vKHL0gDEdg1CRM0h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vKHL0gDEdg1CRM0h .marker.cross{stroke:#333333;}#mermaid-svg-vKHL0gDEdg1CRM0h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vKHL0gDEdg1CRM0h p{margin:0;}#mermaid-svg-vKHL0gDEdg1CRM0h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster-label text{fill:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster-label span{color:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster-label span p{background-color:transparent;}#mermaid-svg-vKHL0gDEdg1CRM0h .label text,#mermaid-svg-vKHL0gDEdg1CRM0h span{fill:#333;color:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h .node rect,#mermaid-svg-vKHL0gDEdg1CRM0h .node circle,#mermaid-svg-vKHL0gDEdg1CRM0h .node ellipse,#mermaid-svg-vKHL0gDEdg1CRM0h .node polygon,#mermaid-svg-vKHL0gDEdg1CRM0h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vKHL0gDEdg1CRM0h .rough-node .label text,#mermaid-svg-vKHL0gDEdg1CRM0h .node .label text,#mermaid-svg-vKHL0gDEdg1CRM0h .image-shape .label,#mermaid-svg-vKHL0gDEdg1CRM0h .icon-shape .label{text-anchor:middle;}#mermaid-svg-vKHL0gDEdg1CRM0h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vKHL0gDEdg1CRM0h .rough-node .label,#mermaid-svg-vKHL0gDEdg1CRM0h .node .label,#mermaid-svg-vKHL0gDEdg1CRM0h .image-shape .label,#mermaid-svg-vKHL0gDEdg1CRM0h .icon-shape .label{text-align:center;}#mermaid-svg-vKHL0gDEdg1CRM0h .node.clickable{cursor:pointer;}#mermaid-svg-vKHL0gDEdg1CRM0h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vKHL0gDEdg1CRM0h .arrowheadPath{fill:#333333;}#mermaid-svg-vKHL0gDEdg1CRM0h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vKHL0gDEdg1CRM0h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vKHL0gDEdg1CRM0h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vKHL0gDEdg1CRM0h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vKHL0gDEdg1CRM0h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vKHL0gDEdg1CRM0h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster text{fill:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h .cluster span{color:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vKHL0gDEdg1CRM0h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vKHL0gDEdg1CRM0h rect.text{fill:none;stroke-width:0;}#mermaid-svg-vKHL0gDEdg1CRM0h .icon-shape,#mermaid-svg-vKHL0gDEdg1CRM0h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vKHL0gDEdg1CRM0h .icon-shape p,#mermaid-svg-vKHL0gDEdg1CRM0h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vKHL0gDEdg1CRM0h .icon-shape .label rect,#mermaid-svg-vKHL0gDEdg1CRM0h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vKHL0gDEdg1CRM0h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vKHL0gDEdg1CRM0h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vKHL0gDEdg1CRM0h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自定义程序三大风险
① 漏写检查
② = 4 漏判 12/24
③ SELECT 绕过 S_TABU_DIS
程序门户大开
校验形同虚设
数据泄露风险
三点核心结论:
- 自定义程序中的
AUTHORITY-CHECK不会影响标准事务码的行为。两套程序各自独立运行。 - 想让两者一致受控,正确做法是统一使用标准授权对象 (如
Q_INSPTYPE),PFCG 配一份,两端自动同时生效。 - 只配 Z 事务码、不配 QA32 时 ,只要 Z 程序用标准对象
Q_INSPTYPE检查,就是安全的;用自建对象或干脆不检查,就是通过 Z 程序绕过标准权限的安全漏洞。
核心一句话:
不给 QA32 的
S_TCODE,只是"堵住了大门";数据权限的"窗户"是否关好,取决于 Z 程序有没有检查Q_INSPTYPE。
附:本文归档说明
本文源自一次 QM 检验批自建程序的权限设计讨论,涉及检验类型字段名 QPRUEFART 的陷阱、QA32 与自定义程序权限边界等实战话题,连同前序的《一次 ASSIGN_TYPE_CONFLICT 排查记》一并归档,供团队内部参考。
💬 你在自定义程序里写过 AUTHORITY-CHECK 吗?有没有因为漏判 sy-subrc = 12 而被安全审计抓过?有没有遇到过"用户进不了标准事务码,但通过自建程序看到了全量数据"的情况?欢迎分享你的权限设计经验。