安全架构

两层安全共同保护业务

Macula 将安全控制分为外层 Gateway与内层 BFF / 微服务。网关确认调用方身份并执行入口访问规则,微服务验证网关签发的 JWT,再按操作、方法和数据归属实施细粒度授权。

层次负责什么典型能力
IAM / System登录与身份、角色、菜单、按钮及接口关联、应用凭据外部 Token、权限规则与应用信息
Gateway外部认证、入口访问控制、内部 JWT 转换Token、AK/SK、API Key,URL 角色规则
BFF / ServiceJWT 验签、业务方法和数据级控制SecurityUtils、@PreAuthorize、@PostAuthorize、@CheckOwner

前端菜单或按钮的可见性方便用户操作;真正的请求授权由网关和服务端共同完成。

安全原理图

Macula 外层网关与内层微服务安全架构客户端以 Token、AK/SK 或 API Key 进入网关。Token 经 IAM 认证与角色 URL 规则授权;AK/SK 经签名和应用 URL 范围检查;API Key 校验应用身份。网关生成内部 JWT,BFF 和业务服务使用网关 JWK 验签。认证上下文可由 SecurityUtils 读取。执行前进行 PreAuthorize 和 CheckOwner 入参资源校验,业务查询落实数据范围,返回后进行 PostAuthorize 或 CheckOwner 结果归属校验。浏览器 / OAuth2 调用方Bearer Token系统调用方AK/SK · HMAC 签名应用调用方API Key外层 · GatewayIAM 提供身份,System / Redis 提供权限与应用信息Token 认证IAM introspection读取用户 / 角色声明AK/SK 认证HMAC 签名、Date应用允许的 URL 范围API Key 认证Bearer sk-...校验 Redis 应用记录URL 角色授权菜单 / 角色 / URL 规则系统调用通道保留应用身份,不自动赋予登录用户角色网关签发内部 JWT身份、角色 / scope、租户:按认证方式提供Authorization: Bearer <JWT>网关 JWK 公钥验签内层 · BFF / 业务微服务Security Starter 验证网关 JWT建立 SecurityContext 认证上下文执行前授权(按业务注解)@PreAuthorize@CheckOwner("#p0")操作权限 / 资源 PasskeySecurityUtils读取身份、角色、租户辅助业务判断,读取不等于鉴权执行业务方法查询 / 写操作落实数据范围与业务条件查询返回时返回后校验 · 用于无副作用的查询@PostAuthorize@CheckOwner("returnObject.ownerUsername")外层与内层共同生效;列表范围与租户边界仍由业务落实
网关处理入口认证与访问规则,微服务继续校验 JWT 和业务权限。窄屏可横向滚动,或单独打开原理图查看。

原理图对应的关键配置

以下按图中的网关、内部 JWT 与微服务授权三个位置说明最小接入配置。环境变量由部署环境提供,完整参数分别见 Gateway 和 Security。

网关:IAM 认证与入口权限

网关使用 IAM introspection 验证外部 Token。示例同时开启 API Key 入口,并为 Token 的 URL 权限选择严格策略;启用前先在 System 中维护应用、URL 与角色关系。

spring:
  security:
    oauth2:
      resourceserver:
        opaquetoken:
          introspection-uri: ${IAM_INTROSPECTION_URI}
          client-id: ${IAM_CLIENT_ID}
          client-secret: ${IAM_CLIENT_SECRET}
macula:
  gateway:
    security:
      ignore-urls: []
      only-auth-urls: []
      default-url-require-check: true  # 此例选用严格策略,默认 false
    apikey:
      enabled: true

macula.gateway.security 下的三项策略分别是:

配置默认值含义
ignore-urls空列表,合并内置白名单放行入口认证与 URL 角色检查
only-auth-urls空列表Token 有效即可,不执行该路径的 URL 角色匹配
default-url-require-checkfalse未匹配 URL 角色规则时,是否仍检查角色;设为 true 时,普通角色无匹配规则会被拒绝

macula.gateway.apikey.enabled 默认 true,控制 API Key 认证过滤器。AK/SK 的密钥、应用 URL 允许范围及 API Key 应用记录由平台维护,网关通过 System Redis 读取,接入方式见 Gateway 的独立 System Redis 配置。

微服务:验证网关 JWT

BFF 与业务微服务引入 Security Starter,并指向本应用 Gateway 的 /oauth2/jwks 公钥集:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: ${GATEWAY_JWK_SET_URI}
macula:
  security:
    ignore-urls: []

IAM_INTROSPECTION_URI 是 IAM 的 Token 校验地址,GATEWAY_JWK_SET_URI 是 应用网关的 JWT 公钥地址,两者对应不同环节。网关的放行配置与微服务自己的 macula.security.ignore-urls 分别生效;通过网关不表示已经通过所有业务授权。

Security Starter 已开启方法安全,SecurityUtils 读取验签后的上下文;@PreAuthorize、@PostAuthorize 和 @CheckOwner 按需声明在 Spring Bean 的业务方法上。

平台操作权限与资源凭证

使用 @PreAuthorize("@pms.hasPermission('order:cancel')") 获取平台按钮 / 操作权限时,还需引入 System Starter:

macula:
  cloud:
    endpoint: ${MACULA_CLOUD_ENDPOINT}
    app-key: ${MACULA_CLOUD_APP_KEY}
    secret-key: ${MACULA_CLOUD_APP_SECRET}

endpoint 指向 Macula Cloud 网关基础地址,客户端会追加 /system;app-key 与 secret-key 为平台登记的应用调用凭据。单纯按 JWT 角色 / scope 授权不需要依赖 PMS 查询,详见 System。

使用 @CheckOwner("#p0") 在执行前验证资源 Passkey 时,再开启:

macula:
  security:
    passkey:
      enabled: true
      ttl: 5m
      secret-base64: ${PASSKEY_SECRET_BASE64}

Passkey 默认关闭,默认有效期为 5 分钟;密钥为独立服务端密钥,Base64 解码后至少 32 字节。授权列表 DTO 实现 PasskeyResource,配合 Web Starter 签发,客户端通过 X-Resource-Passkey 携带凭证。@CheckOwner("returnObject.ownerUsername") 的返回值归属模式不依赖开启 Passkey,完整规则见 Security。

外层:认证入口与 URL 权限

入口网关确认的身份当前 main 的访问控制路径
TokenIAM 返回的用户或 OAuth2 应用身份,以及角色等声明IAM introspection;按配置执行角色与菜单 / URL 规则
AK/SK通过 HMAC 签名确认的应用身份校验应用密钥、请求签名、Date 和应用允许的 URL 范围
API KeyAPI Key 对应的应用身份校验平台 Redis 中的应用记录,再进入系统调用通道

Token 请求的 URL 权限来自 System 维护的菜单 / 角色 / 接口关联,网关据此匹配当前身份的角色。白名单、仅认证路径和未匹配规则的处理由网关策略决定,详见 Gateway。

AK/SK、API Key 不会自动变成登录用户,也不会自动得到用户角色。 当前实现将它们作为系统调用路径;AK/SK 还使用应用自己的 URL 允许范围。需要沿用 Token 角色授权的应用,可按平台登记采用带相应授权声明的 OAuth2 Token。

中间:统一携带网关 JWT

认证和对应访问规则通过后,Gateway 将可信身份转换为内部 JWT,通过 Authorization: Bearer <JWT> 传给后端。JWT 中的身份、角色、scope、租户等信息取决于认证方式与实际声明,不是每一种应用凭据都具有全部用户信息。

BFF、Service、Basic、OpenAPI、Thirdparty 等后端运行模块通过应用网关的 JWK 公钥集验证 JWT,并建立 Spring Security 认证上下文。此时 SecurityUtils 可读取当前身份、角色、租户等信息;读取上下文本身不执行授权。

内层:操作、方法与数据归属

能力作用
@PreAuthorize方法执行前检查角色、scope 或按钮 / 操作权限
@PostAuthorize方法返回后根据真实结果检查条件,适用于无副作用的查询
@CheckOwner返回后检查资源拥有者,或在执行前验证指定资源的 Passkey
业务查询与事务限定列表、分页、租户、数据范围与业务状态

例如,网关先判断角色是否能访问应用管理 URL,BFF 再通过 @PreAuthorize("@pms.hasPermission('...')") 检查具体按钮 / 操作权限;业务方法按需要使用 @CheckOwner 判断单资源归属或资源凭证。

@PostAuthorize 与返回值归属检查发生在方法执行后,不用于阻止写入副作用;列表范围和租户边界仍要由业务查询限定。具体注解用法、角色映射和 Passkey 配置统一见 Security。

继续阅读

  • 快速开始:IAM introspection、Cloud endpoint、网关 JWK 三项最小接入配置。
  • Gateway:三种认证入口、URL 权限与 JWT 转换的详细配置。
  • Security:SecurityUtils、方法授权与资源归属控制。
  • Samples 配置规范:实际应用中的配置分层和地址选择。