第2章 策略语言基础:JSON结构解析

权限策略这东西,说白了就是一堆JSON。你想想看,云厂商那么多,为什么策略语言长得都差不多?因为它们都借鉴了AWS IAM的那套设计。我最早接触策略配置时,第一反应是「这不就是个JSON嘛,有什么好学的」——结果第一次上线就把存储桶权限配成了公开读写,差点被运维同事请喝茶。

嗯,咱们今天就把这个JSON结构拆开揉碎了讲。你掌握了这章,后面所有策略配置都会变得很自然。

2.1 策略的骨架:Version + Statement

一个完整的权限策略,其实就两个顶层字段:Version 和 Statement。我习惯把Version想象成「协议版本号」,Statement就是「具体的规则列表」。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "oss:GetObject",
      "Resource": "acs:oss:*:*:my-bucket/*"
    }
  ]
}

你看,结构非常清晰。Version固定写死,Statement是一个数组——这意味着你可以放多条规则。我在项目中见过有人把几十条Statement塞到一个策略里,说实话,可读性很差。我建议每条策略控制在5条Statement以内,超过就拆成多个策略。

核心要点:Version目前主流是 2012-10-17,部分老系统还在用 2008-10-17。新写策略一律用新的,别问为什么,问就是兼容性。

2.2 Statement结构拆解

Statement里的每个对象,都有几个固定字段。我列个表,你一眼就能看明白:

字段 是否必填 说明
Effect 必填 Allow 或 Deny,决定是允许还是拒绝
Action 必填 操作列表,比如 oss:GetObject、ecs:DescribeInstances
Resource 必填 资源ARN,指定这条规则作用在哪些资源上
Condition 选填 条件约束,比如IP限制、时间限制
Principal 选填 主体,通常用在资源策略里,身份策略不需要

这里有个坑——Principal字段。我刚开始做跨账号授权时,死活想不通为什么策略不生效。后来发现,身份策略(挂在用户/角色上的)不需要写Principal,只有资源策略(挂在存储桶/队列上的)才需要。这个区别,你记一下,后面章节会反复用到。

2.3 Effect字段:Allow vs Deny

Effect只有两个值:Allow 和 Deny。看起来简单,但它们的优先级关系,很多人搞混。

我直接说结论:Deny优先于Allow。哪怕你写了100条Allow,只要有一条Deny命中,结果就是拒绝。为什么会这样?因为安全设计上,拒绝必须比允许更「强硬」,否则你没法做例外控制。

我曾经踩过的坑:给一个运维账号配了全读写权限(Allow *),但忘了某个API默认有Deny策略。结果那个账号所有操作都正常,唯独不能调用某个关键接口。排查了整整一下午,最后发现是系统内置的一条Deny策略在作祟。所以,遇到权限异常,先查Deny。

Effect的评估逻辑,我画了张图,你一看就懂:

Effect 评估流程图 收到访问请求 是否有 Deny 策略命中? (检查所有显式+隐式Deny) 拒绝访问 检查 Allow 策略 有 Allow 命中? (显式Allow) 允许访问 默认拒绝 说明:显式 Deny → 显式 Allow → 默认 Deny(隐式拒绝)

这个流程你记住三个字:显式Deny > 显式Allow > 隐式Deny。隐式Deny就是「你没写Allow,那就默认拒绝」。很多新手以为没写Deny就是允许,其实恰恰相反——没写Allow就是拒绝。

2.4 实战:写一个最小策略

光说不练假把式。咱们写一个真实场景的策略:允许某个用户读取指定存储桶中的文件。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "oss:GetObject",
        "oss:ListObjects"
      ],
      "Resource": [
        "acs:oss:*:*:my-bucket",
        "acs:oss:*:*:my-bucket/*"
      ]
    }
  ]
}

注意两个细节:

  • Action用了数组——虽然只有两个操作,但用数组写更规范,方便以后扩展
  • Resource写了两条——一条给存储桶本身(用于ListObjects),一条给桶内文件(用于GetObject)。我见过有人只写了桶的ARN,结果GetObject一直403

我的小技巧:写Resource时,如果拿不准ARN格式,先去云厂商的控制台复制。每个资源都有对应的ARN模板,手写容易漏掉分区或账号ID。我早期手写过一次,把 oss:* 写成了 oss:*:,多了一个冒号,策略直接解析失败。

2.5 常见错误与排查思路

策略配置出错,通常就这几种情况:

  1. JSON格式错误——多了一个逗号,少了一个引号。我建议用在线JSON校验工具先过一遍
  2. Version写错——用了旧版本,新功能不识别
  3. Resource遗漏——只写了父资源,没写子资源(比如只写了存储桶,没写桶内对象)
  4. Action名称拼错——大小写敏感,GetObject 写成 getobject 就不认

排查时,我习惯先看「模拟验证」工具。大部分云平台都提供策略模拟器,你把策略贴进去,选一个操作,它会告诉你「Allow还是Deny,以及为什么」。这个工具救过我很多次。

嗯,JSON结构这部分就这些。你只要记住Version固定、Statement是数组、Effect只有Allow和Deny、Deny优先,后面学Condition和Principal就会轻松很多。