Spring Boot 3+Keycloak:如何按用户组设置并限制资源服务器作用域?
我正在开发基于Spring Boot 3和Spring Security 6的REST API应用,采用Spring Security资源服务器做授权,端点通过注解@PreAuthorize("hasAuthority('SCOPE_scope-name')")限制访问,使用OIDC、JWT协议,以Keycloak作为授权服务器。
需求:
- 按用户组限制资源访问:用户a、b、c需拥有
read:test和write:test作用域,用户x、y仅拥有read:test作用域,即使x、y请求write:test,Keycloak也不应发放该作用域。 - 希望通过用户组管理用户,创建对应角色并分配给组,每个角色关联允许的作用域。但查阅文档后因作用域与角色的多位置配置感到困惑,不知如何实现。
另外疑问:根据Spring Security文档,仅需在application.properties中配置spring.security.oauth2.resourceserver.jwt.issuer-uri,但不理解Spring应用仅通过该配置如何识别Keycloak中对应的客户端/资源服务器。
核心问题:在Keycloak中,如何设置、管理资源服务器的API端点作用域,并按用户进行限制?
一、Keycloak端完整配置步骤
1. 定义资源服务器的作用域
- 登录Keycloak控制台,进入目标Realm
- 左侧菜单选择「Clients」,找到API对应的客户端(即资源服务器端的客户端),进入「Client scopes」标签页
- 点击「Create client scope」,分别创建
read:test和write:test两个作用域:- 类型选「Optional」(后续通过角色控制是否发放),完成保存后,这两个作用域会归属当前客户端的可用作用域列表
2. 创建关联作用域的角色
- 左侧菜单选择「Roles」,点击「Create role」:
- 创建
test-full-access角色,描述为「拥有test资源的读写权限」 - 创建
test-read-only角色,描述为「仅拥有test资源的只读权限」
- 创建
- 进入每个角色的「Client scopes」标签页,为角色绑定对应作用域:
- 给
test-full-access添加read:test和write:test作用域 - 给
test-read-only只添加read:test作用域
- 给
3. 用用户组批量管理权限
- 左侧菜单选择「Groups」,点击「New group」:
- 创建
test-full-users组,进入组的「Role mappings」标签页,添加test-full-access角色 - 创建
test-read-only-users组,进入组的「Role mappings」标签页,添加test-read-only角色
- 创建
- 将用户a、b、c加入
test-full-users组,用户x、y加入test-read-only-users组
4. 配置作用域发放的权限规则
- 回到API客户端的「Settings」标签页,找到「Authorization」部分,开启「Authorization Enabled」
- 进入「Authorization」标签页,依次配置策略与权限:
- 策略配置:选择「Policies」→「Create policy」→「Role policy」:
- 策略1:名称
allow-test-full,关联test-full-access角色,逻辑设为「Positive」 - 策略2:名称
allow-test-read,关联test-read-only角色,逻辑设为「Positive」
- 策略1:名称
- 权限配置:选择「Permissions」→「Create permission」→「Scope permission」:
- 权限1:名称
grant-read-test,关联read:test作用域,绑定allow-test-full和allow-test-read策略 - 权限2:名称
grant-write-test,关联write:test作用域,仅绑定allow-test-full策略
完成后,只有test-full-users组的用户能获取write:test作用域,只读组用户请求该作用域会被Keycloak直接拒绝。
- 权限1:名称
- 策略配置:选择「Policies」→「Create policy」→「Role policy」:
二、关于Spring Boot中issuer-uri的疑问解答
Spring Security通过issuer-uri配置,会自动请求Keycloak的/.well-known/openid-configuration端点获取元数据,其中包含JWT公钥、授权端点等核心信息。这里的逻辑是:
- Spring Boot作为资源服务器,核心是验证JWT的合法性(签名、签发方、过期时间等),以及JWT中的
scope声明是否匹配端点的@PreAuthorize规则,不需要提前指定客户端ID - 如果需要限制仅特定客户端能访问API,才需要额外配置
spring.security.oauth2.resourceserver.jwt.audiences,指定Keycloak中对应的客户端ID,Spring会校验JWT的aud(受众)声明是否匹配
三、Spring Boot端补充配置
确保application.properties中包含基础配置,按需添加客户端限制:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://your-keycloak-domain/realms/your-realm # 如需限制特定客户端访问,添加以下配置 # spring.security.oauth2.resourceserver.jwt.audiences=your-api-client-id
端点权限控制保持使用@PreAuthorize("hasAuthority('SCOPE_read:test')")和@PreAuthorize("hasAuthority('SCOPE_write:test')")即可,Spring Security会自动解析JWT中的scope字段,添加SCOPE_前缀作为权限标识。
内容的提问来源于stack exchange,提问作者Mirza Prangon

