实际上,我可以从定义如下的 YAML 文件加载/注入地图:
# custom-auth.yaml
auth:
providers:
keycloak:
enabled: true
realmUrl: http://localhost:38087/auth/realms/my-app-realm
clientId: keycloak-client-id
clientSecret: keycloak-client-secret
logoutUrl: keycloak-logout-url
trustbuilder:
enabled: false
clientId: tb-1-client-id
clientSecret: tb-1-client-secret
logoutUrl: tb-1-logout-url
配置类定义如下:
@Data
@Configuration
@ConfigurationProperties(prefix = "auth")
@PropertySource(value="classpath:custom-auth.yaml", factory = AuthYamlFactory.class)
public class OIDCConfiguration {
private Map<String, OIDCProvider> providers;
@Data
public static class OIDCProvider {
private boolean enabled;
private String realmUrl;
private String clientId;
private String clientSecret;
private String logoutUrl;
}
}
我们需要一个工厂类来加载上面的自定义 YAML 文件:
public class AuthYamlFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
测试证明有效:
@SpringBootTest
public class OIDCConfigurationTest {
@Autowired
private OIDCConfiguration oidcConfiguration;
@Test
public void shouldReadSeparateValues() {
Map<String, OIDCConfiguration.OIDCProvider> providers = oidcConfiguration.getProviders();
assertThat(providers).containsOnlyKeys("keycloak", "trustbuilder");
assertThat(providers.get("keycloak").isEnabled()).isTrue();
assertThat(providers.get("keycloak").getClientId()).isEqualTo("keycloak-client-id");
System.out.println(providers.get("keycloak"));
}
...
}
我想知道为什么我不能实现相同但使用
application.properties
文件定义如下:
auth.providers.keycloak.enabled=true
auth.providers.keycloak.clientSecret=keycloak-client-secret
auth.providers.keycloak.clientId=keycloak-client-id
auth.providers.trustbuilder.enabled=false
auth.providers.trustbuilder.clientSecret=trustbuilder-client-secret
auth.providers.trustbuilder.clientId=trustbuilder-client-id
配置类会有所不同,因为我无法将字符串映射到
OIDCProvider
实例:
@Data
@Configuration
@ConfigurationProperties("auth")
public class OIDCPropertiesConfiguration {
private Map<String, String> providers;
...
}
测试班:
@SpringBootTest
public class OIDCConfigurationTest {
@Autowired
private OIDCConfiguration oidcConfiguration;
@Autowired
private OIDCPropertiesConfiguration oidcPropertiesConfiguration;
...
@Test
public void shouldReadSeparateValuesFromPropertiesFile() {
Map<String, String> providers = oidcPropertiesConfiguration.getProviders();
assertThat(providers).containsOnlyKeys("keycloak", "trustbuilder");
assertThat(providers.get("keycloak.enabled")).isEqualTo(true);
assertThat(providers.get("keycloak.clientId")).isEqualTo("keycloak-client-id");
System.out.println(providers.get("keycloak"));
}
}
上面的测试失败了,因为我在这种提供者中有 6 个,而不是 2 个提供者地图:
{
keycloak.enabled=true,
keycloak.clientSecret=keycloak-client-secret,
keycloak.clientId=keycloak-client-id,
trustbuilder.enabled=false,
trustbuilder.clientSecret=trustbuilder-client-secret,
trustbuilderclientId=trustbuilder-client-id
}
使用索引值更改
application.properties
文件中的数据,如下所示:
auth.providers[0].enabled=true
auth.providers[0].clientSecret=keycloak-client-secret
auth.providers[0].clientId=keycloak-client-id
auth.providers[1].enabled=true
auth.providers[1].clientSecret=keycloak-client-secret
auth.providers[1].clientId=keycloak-client-id
没有改变结果,地图中仍然有 6 个条目,而不是 2 个。
为什么会这样? 以及如何实现与 YAML 文件相同的效果?有什么线索吗?
只是一个想法:
在您的属性文件中添加一个属性说“
auth.providers
”和作为
一个值把一个 JSON 对象作为一个包含你的上下文的字符串
地图
在您的代码中将该值作为字符串读取
@Value("${auth.providers}" private String authProvidersStr
使用任何 Json 库将 Json 对象解析为地图。 无论您喜欢什么,这都可以使用 Json Jackson 库或 Gson 库轻松完成。我编写了自己的 JsonUtils 辅助实用程序,它是 Json-Jackson 库的
ObjectMapper
类的精简包装器。使用此实用程序,您的代码将非常简单:
try {
Map<String, Object> authProviders = JsonUtils.readObjectFromJsonString(authProvidersStr, Map.class);
catch(IOException ioe) {
...
}
JsonUtils
类是我编写和维护的开源 MgntUtils java 库的一部分。这里是JsonUtils Javadoc,库可以作为maven artifact或从github(包括源代码和Javadoc)获得
最后,我想出了如何让它发挥作用。 我们开始:
application.properties
文件中的值如下:auth.providers.keycloak.name=dev.server.com
auth.providers.keycloak.url=https://dev.server.com
auth.providers.trustbuilder.name=prod.server.com
auth.providers.trustbuilder.url=https://prod.server.com
@Configuration
@ConfigurationProperties("auth")
public class AuthConfig {
// The annotation processor automatically considers inner classes as nested properties.
private Map<String, AuthProvider> providers = new HashMap<>();
public Map<String, AuthProvider> getProviders() {
return providers;
}
public void setProviders(Map<String, AuthProvider> providers) {
this.providers = providers;
}
public static class AuthProvider {
String name;
String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
@SpringBootTest
class AuthConfigTest {
@Autowired
private AuthConfig authConfig;
@Test
public void authProvidersShouldContainRightValues() {
Map<String, AuthConfig.AuthProvider> providersData = authConfig.getProviders();
Set<String> providers = providersData.keySet();
assertThat(providers).containsExactly("keycloak", "trustbuilder");
}
@Test
public void providerShouldHaveAttributes() {
Map<String, AuthConfig.AuthProvider> providersData = authConfig.getProviders();
assertEquals(providersData.get("keycloak").getName(), "dev.server.com");
assertEquals(providersData.get("keycloak").getUrl(), "https://dev.server.com");
}
}
当然,我们可以在 Spring Boot 应用程序中的任何位置访问配置值。这是它在虚拟控制器中的使用示例:
@RestController
public class HelloController {
private final AuthConfig authConfig;
public HelloController(AuthConfig authConfig) {
this.authConfig = authConfig;
}
@GetMapping("/greeting")
public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
System.out.println("++++++++++++++++++");
Map<String, AuthConfig.AuthProvider> servers = authConfig.getProviders();
Set<Map.Entry<String, AuthConfig.AuthProvider>> entries = servers.entrySet();
for (Map.Entry<String, AuthConfig.AuthProvider> entry : entries) {
System.out.println(entry.getKey());
System.out.println( entry.getValue());
}
return String.format("Hello %s!", name);
}
}
Et voilà。希望这可以帮助。 瞧。