Spring Boot集成Keycloak认证授权——策略执行器配置优化
解决Keycloak Policy Enforcer配置硬编码问题
问题背景
在Spring Boot应用中完全基于Keycloak实现认证授权,当前通过policy-enforcer.json配置Keycloak策略执行器,但JSON文件中的realm、认证服务器地址、客户端信息均为硬编码,无法使用占位符动态替换。需要将这些配置项迁移到application.properties或配置类中读取,同时保留Keycloak处理所有认证授权逻辑的核心机制。
当前代码实现:
SecurityConfig类
import org.keycloak.adapters.authorization.integration.jakarta.ServletPolicyEnforcerFilter; import org.keycloak.adapters.authorization.spi.ConfigurationResolver; import org.keycloak.adapters.authorization.spi.HttpRequest; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; import org.keycloak.util.JsonSerialization; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.web.SecurityFilterChain; import java.io.IOException; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable); httpSecurity.addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter.class); httpSecurity.sessionManagement(t -> t.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return httpSecurity.build(); } private ServletPolicyEnforcerFilter createPolicyEnforcerFilter() { return new ServletPolicyEnforcerFilter(new ConfigurationResolver() { @Override public PolicyEnforcerConfig resolve(HttpRequest httpRequest) { try { return JsonSerialization.readValue(getClass().getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig.class); } catch (IOException e) { throw new RuntimeException(e); } } }); } }
policy-enforcer.json文件
{ "realm": "realm-name", "auth-server-url": "server-url", "resource": "client-name", "credentials": { "secret": "client-secret" }, "http-method-as-scope": true, "paths": [ { "path": "/path-1", "enforcement-mode": "DISABLED" }, { "path": "/path-2", "enforcement-mode": "DISABLED" } ] }
优化方案
1. 在application.properties中添加Keycloak配置项
将硬编码的配置参数移到配置文件中:
# Keycloak核心配置 keycloak.realm=realm-name keycloak.auth-server-url=server-url keycloak.resource=client-name keycloak.credentials.secret=client-secret keycloak.http-method-as-scope=true # 可选:路径规则配置(也可保留在JSON中) keycloak.paths[0].path=/path-1 keycloak.paths[0].enforcement-mode=DISABLED keycloak.paths[1].path=/path-2 keycloak.paths[1].enforcement-mode=DISABLED
2. 创建配置类绑定属性
通过@ConfigurationProperties注解将配置文件中的属性绑定到Java类:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; @Component @ConfigurationProperties(prefix = "keycloak") public class KeycloakProperties { private String realm; private String authServerUrl; private String resource; private Credentials credentials; private boolean httpMethodAsScope; private List<PathConfig> paths; // Getters & Setters public static class Credentials { private String secret; public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } } public static class PathConfig { private String path; private String enforcementMode; public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getEnforcementMode() { return enforcementMode; } public void setEnforcementMode(String enforcementMode) { this.enforcementMode = enforcementMode; } } public String getRealm() { return realm; } public void setRealm(String realm) { this.realm = realm; } public String getAuthServerUrl() { return authServerUrl; } public void setAuthServerUrl(String authServerUrl) { this.authServerUrl = authServerUrl; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public Credentials getCredentials() { return credentials; } public void setCredentials(Credentials credentials) { this.credentials = credentials; } public boolean isHttpMethodAsScope() { return httpMethodAsScope; } public void setHttpMethodAsScope(boolean httpMethodAsScope) { this.httpMethodAsScope = httpMethodAsScope; } public List<PathConfig> getPaths() { return paths; } public void setPaths(List<PathConfig> paths) { this.paths = paths; } }
3. 修改SecurityConfig动态构建配置
注入配置类,动态替换PolicyEnforcerConfig中的硬编码信息:
import org.keycloak.adapters.authorization.integration.jakarta.ServletPolicyEnforcerFilter; import org.keycloak.adapters.authorization.spi.ConfigurationResolver; import org.keycloak.adapters.authorization.spi.HttpRequest; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; import org.keycloak.util.JsonSerialization; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; import org.springframework.security.web.SecurityFilterChain; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; @Configuration @EnableWebSecurity public class SecurityConfig { private final KeycloakProperties keycloakProperties; // 构造注入配置属性 public SecurityConfig(KeycloakProperties keycloakProperties) { this.keycloakProperties = keycloakProperties; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable); httpSecurity.addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter.class); httpSecurity.sessionManagement(t -> t.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return httpSecurity.build(); } private ServletPolicyEnforcerFilter createPolicyEnforcerFilter() { return new ServletPolicyEnforcerFilter(new ConfigurationResolver() { @Override public PolicyEnforcerConfig resolve(HttpRequest httpRequest) { try { // 初始化配置(可选择保留JSON中的路径配置,或完全从配置类读取) PolicyEnforcerConfig config; if (keycloakProperties.getPaths() == null || keycloakProperties.getPaths().isEmpty()) { // 从JSON读取路径配置 config = JsonSerialization.readValue( getClass().getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig.class ); } else { // 完全从配置类构建 config = new PolicyEnforcerConfig(); // 转换路径配置 List<PolicyEnforcerConfig.PathConfig> policyPaths = keycloakProperties.getPaths().stream() .map(propPath -> { PolicyEnforcerConfig.PathConfig pathConfig = new PolicyEnforcerConfig.PathConfig(); pathConfig.setPath(propPath.getPath()); pathConfig.setEnforcementMode(propPath.getEnforcementMode()); return pathConfig; }) .collect(Collectors.toList()); config.setPaths(policyPaths); } // 覆盖核心配置项 config.setRealm(keycloakProperties.getRealm()); config.setAuthServerUrl(keycloakProperties.getAuthServerUrl()); config.setResource(keycloakProperties.getResource()); PolicyEnforcerConfig.Credentials credentials = new PolicyEnforcerConfig.Credentials(); credentials.setSecret(keycloakProperties.getCredentials().getSecret()); config.setCredentials(credentials); config.setHttpMethodAsScope(keycloakProperties.isHttpMethodAsScope()); return config; } catch (IOException e) { throw new RuntimeException("加载策略执行器配置失败", e); } } }); } }
4. 可选:移除policy-enforcer.json文件
如果所有配置都已迁移到application.properties,可以直接删除policy-enforcer.json文件,完全通过配置类动态构建PolicyEnforcerConfig。
内容的提问来源于stack exchange,提问作者Adrienn
相关产品推荐
相关产品推荐

