|
4 | 4 | import io.github.easy4j.hermes.api.model.*; |
5 | 5 | import io.github.easy4j.hermes.cli.HermesCli; |
6 | 6 | import io.github.easy4j.hermes.cli.HermesCliExecutor; |
| 7 | +import io.github.easy4j.hermes.security.ProfileBinding; |
| 8 | +import io.github.easy4j.hermes.security.ProfileCredentialResolver; |
| 9 | +import io.github.easy4j.hermes.security.ProfileIdentity; |
7 | 10 | import io.github.easy4j.hermes.api.HermesHttpClient; |
8 | 11 | import io.github.easy4j.hermes.api.HermesChatClient; |
9 | 12 | import io.github.easy4j.hermes.api.HermesSseClient; |
@@ -95,6 +98,7 @@ public class HermesClient implements AutoCloseable { |
95 | 98 | public HermesClient(HermesClientConfig config) { |
96 | 99 | this(Objects.requireNonNull(config, "config").getHttp(), config.getCli(), new JsonMapper(), |
97 | 100 | HermesOkHttpClientFactory.create(config.getHttp()), true); |
| 101 | + this.config.setProfileCredentialResolver(config.getProfileCredentialResolver()); |
98 | 102 | } |
99 | 103 |
|
100 | 104 | /** |
@@ -126,6 +130,7 @@ public HermesClient(HermesClientConfig config, ObjectMapper objectMapper, OkHttp |
126 | 130 | objectMapper, |
127 | 131 | httpClient, |
128 | 132 | false); |
| 133 | + this.config.setProfileCredentialResolver(config.getProfileCredentialResolver()); |
129 | 134 | } |
130 | 135 |
|
131 | 136 | /** |
@@ -326,6 +331,8 @@ private static void copyHttpConfig(HermesHttpClientConfig src, HermesHttpClientC |
326 | 331 | target.setEndpointPolicy(src.getEndpointPolicy()); |
327 | 332 | target.setBaseUrl(src.getBaseUrl()); |
328 | 333 | target.setApiKey(src.getApiKey()); |
| 334 | + target.setCredentialProvider(src.getCredentialProvider()); |
| 335 | + target.setProfileIdentity(src.getProfileIdentity()); |
329 | 336 | target.setConnectTimeoutMillis(src.getConnectTimeoutMillis()); |
330 | 337 | target.setReadTimeoutMillis(src.getReadTimeoutMillis()); |
331 | 338 | target.setWriteTimeoutMillis(src.getWriteTimeoutMillis()); |
@@ -1069,16 +1076,47 @@ public HermesClient forProfile(String profileId) { |
1069 | 1076 | throw new IllegalStateException("Hermes HTTP client is disabled"); |
1070 | 1077 | } |
1071 | 1078 | String normalizedProfileId = normalizeProfileId(profileId); |
1072 | | - // 并发访问同一 profile 时只发布一个托管视图。 |
1073 | | - return profileClients.computeIfAbsent(normalizedProfileId, this::createProfileClient); |
| 1079 | + ProfileCredentialResolver resolver = config.getProfileCredentialResolver(); |
| 1080 | + if (resolver == null) { |
| 1081 | + throw new IllegalStateException("No credential is configured for Hermes profile " |
| 1082 | + + normalizedProfileId + "; root credentials are never inherited by named profiles"); |
| 1083 | + } |
| 1084 | + ProfileBinding binding = resolver.resolve(normalizedProfileId); |
| 1085 | + if (binding == null) { |
| 1086 | + throw new IllegalStateException("No credential is configured for Hermes profile " + normalizedProfileId); |
| 1087 | + } |
| 1088 | + if (!normalizedProfileId.equals(binding.getProfileId())) { |
| 1089 | + throw new IllegalStateException("Profile credential resolver returned a binding for " |
| 1090 | + + binding.getProfileId() + " instead of " + normalizedProfileId); |
| 1091 | + } |
| 1092 | + return forProfile(binding); |
| 1093 | + } |
| 1094 | + |
| 1095 | + public HermesClient forProfile(ProfileBinding binding) { |
| 1096 | + Objects.requireNonNull(binding, "binding"); |
| 1097 | + if (managedProfileView) { |
| 1098 | + throw new IllegalStateException("Cannot create a profile client from another profile client"); |
| 1099 | + } |
| 1100 | + if (closed.get()) { |
| 1101 | + throw new IllegalStateException("HermesClient is closed"); |
| 1102 | + } |
| 1103 | + if (!isHttpEnabled()) { |
| 1104 | + throw new IllegalStateException("Hermes HTTP client is disabled"); |
| 1105 | + } |
| 1106 | + String profileId = normalizeProfileId(binding.getProfileId()); |
| 1107 | + String cacheKey = config.getHttp().getBaseUrl() + "|" + profileId + "|" + binding.getCredentialIdentity(); |
| 1108 | + return profileClients.computeIfAbsent(cacheKey, ignored -> createProfileClient(binding, profileId)); |
1074 | 1109 | } |
1075 | 1110 |
|
1076 | | - private HermesClient createProfileClient(String profileId) { |
| 1111 | + private HermesClient createProfileClient(ProfileBinding binding, String profileId) { |
1077 | 1112 | HermesHttpClientConfig profileConfig = new HermesHttpClientConfig(); |
1078 | 1113 | copyHttpConfig(config.getHttp(), profileConfig); |
1079 | | - // profile 只改变 URL 前缀并禁用重复探测,传输和 JSON 配置继续复用根客户端。 |
1080 | 1114 | profileConfig.setBaseUrl(profileServerUrl(config.getHttp().getBaseUrl(), profileId)); |
1081 | 1115 | profileConfig.setStartupCheckEnabled(false); |
| 1116 | + profileConfig.setApiKey(null); |
| 1117 | + profileConfig.setProfileIdentity(new ProfileIdentity( |
| 1118 | + config.getHttp().getBaseUrl(), profileId, binding.getCredentialIdentity())); |
| 1119 | + profileConfig.setCredentialProvider(binding.getCredentialProvider()); |
1082 | 1120 | HermesCliConfig disabledCli = new HermesCliConfig(); |
1083 | 1121 | disabledCli.setEnabled(false); |
1084 | 1122 | return new HermesClient(profileConfig, disabledCli, objectMapper, sharedHttpClient, false, true); |
|
0 commit comments