安全架构
两层安全共同保护业务
Macula 将安全控制分为外层 Gateway与内层 BFF / 微服务。网关确认调用方身份并执行入口访问规则,微服务验证网关签发的 JWT,再按操作、方法和数据归属实施细粒度授权。
| 层次 | 负责什么 | 典型能力 |
|---|---|---|
| IAM / System | 登录与身份、角色、菜单、按钮及接口关联、应用凭据 | 外部 Token、权限规则与应用信息 |
| Gateway | 外部认证、入口访问控制、内部 JWT 转换 | Token、AK/SK、API Key,URL 角色规则 |
| BFF / Service | JWT 验签、业务方法和数据级控制 | SecurityUtils、@PreAuthorize、@PostAuthorize、@CheckOwner |
前端菜单或按钮的可见性方便用户操作;真正的请求授权由网关和服务端共同完成。
安全原理图
原理图对应的关键配置
以下按图中的网关、内部 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-check | false | 未匹配 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 的访问控制路径 |
|---|---|---|
| Token | IAM 返回的用户或 OAuth2 应用身份,以及角色等声明 | IAM introspection;按配置执行角色与菜单 / URL 规则 |
| AK/SK | 通过 HMAC 签名确认的应用身份 | 校验应用密钥、请求签名、Date 和应用允许的 URL 范围 |
| API Key | API 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 配置规范:实际应用中的配置分层和地址选择。