如何使用 SPNEGOAuthentication 配置 Jetty 12 HttpClient 进行代理

问题描述 投票:0回答:1

我正在使用 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;
  1. 创建 SPNEGOAuthentication() 对象时传递的 uri 的正确值应该是什么

  2. 在 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");
    }

}
spring-boot kerberos java-17 spnego jetty-12
1个回答
0
投票

创建 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 作为“主机名”,这实际上总是错了。)

© www.soinside.com 2019 - 2024. All rights reserved.