java 使用 HTTP 客户端的 Kerberos 连接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9885323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Kerberos connection using HTTP Client
提问by Sergei Sokolov
I'm writing HTTP connection with Kerberos authentication. I have "HTTP/1.1 401 Unauthorized". Could you recommend me what I should check? I think there's somethink trick, but I don't see it.
我正在编写带有 Kerberos 身份验证的 HTTP 连接。我有“HTTP/1.1 401 未经授权”。你能推荐我应该检查什么吗?我认为有一些技巧,但我没有看到。
May be I should set header "WWW-Authenticate" with "Negotiate"?
可能我应该将标题“WWW-Authenticate”设置为“Negotiate”吗?
Thank a lot in advanced for any help and ideas.
非常感谢您提供任何帮助和想法。
public class ClientKerberosAuthentication {
public static void main(String[] args) throws Exception {
System.setProperty("java.security.auth.login.config", "login.conf");
System.setProperty("java.security.krb5.conf", "krb5.conf");
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();
httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
List<String> authpref = new ArrayList<String>();
authpref.add(AuthPolicy.BASIC);
authpref.add(AuthPolicy.SPNEGO);
httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1, AuthScope.ANY_REALM, AuthPolicy.SPNEGO),
new UsernamePasswordCredentials("myuser", "mypass"));
System.out.println("----------------------------------------");
HttpUriRequest request = new HttpGet("http://localhost:8084/web-app/webdav/213/_test.docx");
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println("----------------------------------------");
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
System.out.println("----------------------------------------");
// This ensures the connection gets released back to the manager
EntityUtils.consume(entity);
} finally {
httpclient.getConnectionManager().shutdown();
}
}
}
回答by Yves Martin
SPNEGO will not work because you use localhost
as URL hostname.
SPNEGO 将不起作用,因为您localhost
用作 URL 主机名。
Your server is configured for a set of SPNs (or at least one) beginning with HTTP/
registered on the ActiveDirectory service account. You can query them from AD thanks to setspn -l yourServiceAccount
.
您的服务器配置为一组 SPN(或至少一个),HTTP/
以在 ActiveDirectory 服务帐户上注册开始。由于 ,您可以从 AD 查询它们setspn -l yourServiceAccount
。
Your URL must use an effective server hostname known as SPN in ActiveDirectory so that Apache Http Client can negotiate a TGS for this service and send it to your server.
您的 URL 必须使用 ActiveDirectory 中称为 SPN 的有效服务器主机名,以便 Apache Http 客户端可以为此服务协商 TGS 并将其发送到您的服务器。
回答by Avinash Singh
Here is a test client I wrote in my project. This client relies on all encryption types to be enabled on JDK,
这是我在项目中编写的测试客户端。此客户端依赖于在 JDK 上启用的所有加密类型,
If you see following in your logs and your keytab is encrypted at 256 bit default etypes for default_tkt_enctypes: 17 16 23 1 3.
如果您在日志中看到以下内容并且您的密钥表以 256 位默认 etypes 加密 default_tkt_enctypes: 17 16 23 1 3.
then following jar
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.htmlneeds to be downloaded and placed in JDK/jre/lib/security
to enable AES256 bit encryption after that you should see following in logs default etypes for default_tkt_enctypes: 18 17 16 23 1 3.
然后
需要下载并放置以下 jar
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.htmlJDK/jre/lib/security
以启用 AES256 位加密,然后您应该在日志中看到以下内容默认类型为default_tkt_enctypes: 18 17 16 23 1 3.
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Lookup;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.cookie.BasicClientCookie;
Utility class
实用类
public class KerberosHttpClient {
private String principal;
private String keyTabLocation;
public KerberosHttpClient() {}
public KerberosHttpClient(String principal, String keyTabLocation) {
super();
this.principal = principal;
this.keyTabLocation = keyTabLocation;
}
public KerberosHttpClient(String principal, String keyTabLocation, String krb5Location) {
this(principal, keyTabLocation);
System.setProperty("java.security.krb5.conf", krb5Location);
}
public KerberosHttpClient(String principal, String keyTabLocation, boolean isDebug) {
this(principal, keyTabLocation);
if (isDebug) {
System.setProperty("sun.security.spnego.debug", "true");
System.setProperty("sun.security.krb5.debug", "true");
}
}
public KerberosHttpClient(String principal, String keyTabLocation, String krb5Location, boolean isDebug) {
this(principal, keyTabLocation, isDebug);
System.setProperty("java.security.krb5.conf", krb5Location);
}
private static HttpClient buildSpengoHttpClient() {
HttpClientBuilder builder = HttpClientBuilder.create();
Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create().
register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true)).build();
builder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(null, -1, null), new Credentials() {
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public String getPassword() {
return null;
}
});
builder.setDefaultCredentialsProvider(credentialsProvider);
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
public HttpResponse callRestUrl(final String url,final String userId) {
//keyTabLocation = keyTabLocation.substring("file://".length());
System.out.println(String.format("Calling KerberosHttpClient %s %s %s",this.principal, this.keyTabLocation, url));
Configuration config = new Configuration() {
@SuppressWarnings("serial")
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, new HashMap<String, Object>() {
{
put("useTicketCache", "false");
put("useKeyTab", "true");
put("keyTab", keyTabLocation);
//Krb5 in GSS API needs to be refreshed so it does not throw the error
//Specified version of key is not available
put("refreshKrb5Config", "true");
put("principal", principal);
put("storeKey", "true");
put("doNotPrompt", "true");
put("isInitiator", "true");
put("debug", "true");
}
}) };
}
};
Set<Principal> princ = new HashSet<Principal>(1);
princ.add(new KerberosPrincipal(userId));
Subject sub = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());
try {
LoginContext lc = new LoginContext("", sub, null, config);
lc.login();
Subject serviceSubject = lc.getSubject();
return Subject.doAs(serviceSubject, new PrivilegedAction<HttpResponse>() {
HttpResponse httpResponse = null;
@Override
public HttpResponse run() {
try {
HttpUriRequest request = new HttpGet(url);
HttpClient spnegoHttpClient = buildSpengoHttpClient();
httpResponse = spnegoHttpClient.execute(request);
return httpResponse;
} catch (IOException ioe) {
ioe.printStackTrace();
}
return httpResponse;
}
});
} catch (Exception le) {
le.printStackTrace();;
}
return null;
}
public static void main(String[] args) throws UnsupportedOperationException, IOException {
KerberosHttpClient restTest = new KerberosHttpClient("HTTP/[email protected]",
"file://C://Development//test.keytab", true);
HttpResponse response = restTest.callRestUrl("http://test.com/service/employees",
"HTTP/[email protected]");
InputStream is = response.getEntity().getContent();
System.out.println("Status code " + response.getStatusLine().getStatusCode());
System.out.println(Arrays.deepToString(response.getAllHeaders()));
System.out.println(new String(IOUtils.toByteArray(is), "UTF-8"));
}
}
回答by SCBoy
I had the same problem and just found your post. I bookmarked it so I can post an answer when I fixed it. I post a link to my question where someone answered it, so if someone finds this via Google they find the answer:
我有同样的问题,刚刚找到你的帖子。我给它加了书签,这样我就可以在修复它时发布答案。我发布了一个指向我的问题的链接,有人回答了它,所以如果有人通过谷歌找到这个,他们会找到答案:
HttpClient has problem creating the SPN for the AD when the URL has a port.
当 URL 具有端口时,HttpClient 在为 AD 创建 SPN 时出现问题。
See my question + answer here: HttpClient check Kerberos secured webpage. NTLM login didn't work
在此处查看我的问题 + 答案:HttpClient 检查 Kerberos 安全网页。NTLM 登录无效