You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring Boot与Keycloak的细粒度权限同步方案问询

解决方案步骤

一、选择权限同步策略

方式1:通过Keycloak Admin API主动推送权限

适合应用端权限更新后实时同步到Keycloak的场景,当管理员在后台分配/修改用户权限时,直接调用Keycloak接口将权限写入Keycloak用户数据中。

  • 先引入Keycloak Admin Client依赖:
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-admin-client</artifactId>
    <version>${keycloak.version}</version>
</dependency>
  • 编写同步逻辑示例:
@Service
public class KeycloakPermissionSyncService {
    @Value("${keycloak.auth-server-url}")
    private String authServerUrl;
    @Value("${keycloak.realm}")
    private String realm;
    @Value("${keycloak.resource}")
    private String clientId;
    @Value("${keycloak.credentials.secret}")
    private String clientSecret;

    public void syncUserPermissions(String userId, Set<String> permissions) {
        // 初始化Admin Client
        Keycloak keycloak = KeycloakBuilder.builder()
                .serverUrl(authServerUrl)
                .realm(realm)
                .clientId(clientId)
                .clientSecret(clientSecret)
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .build();

        // 将权限作为用户自定义属性存入Keycloak
        UserResource userResource = keycloak.realm(realm).users().get(userId);
        UserRepresentation user = new UserRepresentation();
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("permissions", permissions);
        user.setAttributes(attributes);
        userResource.update(user);

        // 若需将权限映射为Keycloak Client角色(需提前在客户端创建对应角色)
        // ClientResource client = keycloak.realm(realm).clients().findByClientId(clientId).get(0);
        // for (String perm : permissions) {
        //     RoleRepresentation role = client.roles().get(perm).toRepresentation();
        //     userResource.roles().clientLevel(client.getId()).add(Collections.singletonList(role));
        // }
    }
}

方式2:用Keycloak User Storage SPI拉取权限

适合权限数据源固定在Postgres,无需主动推送的场景——Keycloak在用户认证时,自动从应用数据库查询权限并注入用户信息。

  • 核心实现:自定义UserStorageProvider,继承AbstractUserAdapterFederatedStorage,重写getAttributes()或getRoleMappings()方法,从Postgres查询当前用户的权限并返回。

二、配置Keycloak将权限写入JWT Claims

登录Keycloak控制台,进入目标Realm → Clients → 选择你的应用客户端 → Mappers标签页,添加对应映射器:

针对自定义属性存储的权限

  • Mapper Type:选择User Attribute
  • Name:填permissions-mapper
  • User Attribute:填permissions(需和同步时的属性key一致)
  • Token Claim Name:填permissions
  • Claim JSON Type:选JSON(因为权限是集合)
  • 勾选Add to ID token和Add to access token

针对Client角色存储的权限

  • Mapper Type:选择Client Role
  • Name:填client-roles-mapper
  • Client ID:选择你的应用客户端
  • Token Claim Name:填authorities
  • 勾选Add to ID token和Add to access token

三、配置Spring Security解析JWT权限

让Spring Security正确读取JWT中的权限并转换为GrantedAuthority:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 对应Keycloak中设置的Claim名称
        authoritiesConverter.setAuthoritiesClaimName("permissions");
        // 若权限需要前缀(比如用hasRole校验时),可开启此行
        // authoritiesConverter.setAuthorityPrefix("ROLE_");

        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
        return converter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())));
        return http.build();
    }
}

如果权限存在于resource_access.{client-id}.roles中,只需修改authoritiesConverter.setAuthoritiesClaimName("resource_access.your-client-id.roles")即可。

四、验证方法级权限

确保已启用方法级安全:在启动类或配置类上添加@EnableMethodSecurity(Spring Security 6+),或@EnableGlobalMethodSecurity(prePostEnabled = true)(旧版本)。之后即可继续使用@PreAuthorize("hasAuthority('LIST_CURRENCIES')")这类注解完成鉴权。


内容的提问来源于stack exchange,提问作者Ramanan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 03:15:36