我正在使用 Jetty 12.x 并尝试使用代理配置 HttpClient。我需要使用 Kerberos 身份验证 对于代理。
参考文档 https://jetty.org/docs/jetty/12/programming-guide/client/http.html#authentication,没有可用于使用 SPNEGO 身份验证配置代理的帮助。示例示例https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java 也没有帮助。
如下面的代码片段所示,我尝试通过代理访问“http://example.com”,其中代理详细信息为:
String realm = "CAPVANLAB.COM";
String kdc = "capvanlab.com";
String proxyUser = "[email protected]";
String proxyPassword = "Pass@123";
String serviceName = "VanBluecoatS200.capvanlab.com"; //this is bluecoat proxy itself
String proxyHost = "192.168.72.8";
int proxyPort = 8080;
创建 SPNEGOAuthentication() 对象时传递的 uri 的正确值应该是什么
在 SPNEGOAuthentication 对象中设置的 serviceName 的值应该是多少
预计: 使用 HttpUrlConnection 生成的服务的预期 SNameString
实际: 应用程序生成的实际 Sname 这里目标站点作为主机名附加在服务名称的 SPN 中,这是不正确的。 查看 SPNEGOAuthentication.initGSSContext() 的代码,从 httpRequest 中提取的主机名并在生成 SPN 时附加到服务名称中,这似乎不正确。
需要帮助才能使其正常工作。
谢谢,
下面是我尝试过的示例代码。
package com.example.caching;
import org.apache.catalina.authenticator.SpnegoAuthenticator;
import org.eclipse.jetty.client.*;
import org.eclipse.jetty.util.component.LifeCycle;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.net.*;
import java.util.HashMap;
import java.util.Set;
@RestController
@Slf4j
public class CustomerController {
public String uri = "http://example.com";
String serviceType = "HTTP";
String serviceName = "VanBluecoatS200.capvanlab.com";
String scheme = "http";
String nameStr ="Capvanlabdc.Capvanlab.com";
String realm = "CAPVANLAB.COM";
String kdc = "capvanlab.com";
String clientName ="svc.bcaaa";
String proxyUser = "[email protected]";
String proxyPassword = "Pass@123";
String proxyHost = "192.168.72.8";
int proxyPort = 8080;
@GetMapping("/jet")
public String jetHttp() {
log.info(">>> checking kerberos httpClient");
setSystemProperties();
HttpClient httpClient;
try {httpClient = getJettyClient();
httpClient.start();
log.info(">>> httpClient.start() httpClient called");
ContentResponse res = httpClient.GET(uri);
log.info("res.getStatus() {}", res.getStatus());
String result = res.getContentAsString();
new Thread(() -> LifeCycle.stop(httpClient)).start();
return result;
} catch(Exception e){
log.warn(">>> Exception " + e);
return "Exception occurred";
}
}
public void setSystemProperties() {
log.info("Setting Kerberos Realm and KDC");
System.setProperty("java.security.krb5.realm", realm);
System.setProperty("java.security.krb5.kdc", kdc);
System.setProperty("sun.security.krb5.debug", "true");
Configuration.setConfiguration(setJaasConfigParams());
log.info("Kerberos Realm and KDC are succesfull set to the perf qualification ");
}
private Configuration setJaasConfigParams() {
log.info("calling setJaasConfigParams");
return new Configuration() {
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, Object> options = new HashMap<>();
options.put("useTicketCache", "false");
AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
return new AppConfigurationEntry[]{appConfigurationEntry};
}
};
}
private HttpClient getJettyClient() throws URISyntaxException {
HttpClient httpClient = new HttpClient();
configProxyJettyClient(httpClient);
return httpClient;
}
private void configProxyJettyClient(HttpClient httpClient) throws URISyntaxException {
log.info("configProxyJettyClient start");
URI spnegoURI = URI.create("http://" + proxyHost + ":" +proxyPort);
SPNEGOAuthentication spnegoAuth = new SPNEGOAuthentication(spnegoURI);
spnegoAuth.setUserName(clientName + "@" + realm); // Or use Kerberos principal
spnegoAuth.setUserPassword(proxyPassword); // Not required if using keytab
spnegoAuth.setServiceName(serviceName);
AuthenticationStore auth = httpClient.getAuthenticationStore();
auth.addAuthentication(spnegoAuth);
HttpProxy proxy = new HttpProxy(proxyHost, proxyPort);
ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
proxyConfig.addProxy(proxy);
log.info("configProxyJettyClient end");
}
}
创建 SPNEGOAuthentication() 对象时传递的 uri 的正确值应该是什么
构造函数对 URI 所做的唯一事情就是将其传递给 super(),因此它与所有身份验证处理程序相同,在我看来,它应该是
http://ProxyHost:ProxyPort
,就像您当前正在做的那样。
在 SPNEGOAuthentication 对象中设置的 serviceName 的值应该是多少
GSSAPI 服务名称可以采用两种形式:
service@hostname
(GSSAPI 样式)和 service/hostname
(Kerberos 样式)。我相信 Java 更喜欢 GSSAPI 风格,而本机 Windows SSPI 坚持 Kerberos 风格。
对于通过 HTTP(S) 的 SPNEGO,服务通常为
HTTP
(大写)。
对于几乎所有协议(包括 HTTP),主机名通常是服务器的 FQDN(首选小写)。
(此外,Active Directory 不关心大小写,但其他类型的 Kerberos KDC 关心,因此请尽量保持一致。)
重要的是服务名称与 KDC 中定义的 SPN 匹配,以便客户端可以请求正确服务的票证。
因此,如果您使用 AD 并且服务帐户分配了
HTTP/blah.example.com
的 SPN,则 GSSAPI 服务名称同样会是 HTTP/blah.example.com
或 [email protected]
(取决于客户端软件所需的语法)。对于 Java,我会从后者开始,所以:
String serviceName = "[email protected]";
(如果您指定的字符串既没有
@
也没有 /
,那么它就被认为只是服务名称,并且通常会使用 local FQDN 作为“主机名”,这实际上总是错了。)