java 我怎样才能让 jcifs 很好地与 apache 轴一起玩
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/916820/
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
How can I get jcifs to play nicely with apache axis
提问by Ben Hammond
I need to connect Apache Axis 1.4 to a Webservice that uses NTLM authentication to restrict access to its operations. I'm expecting to use Samba Jcifs to handle the NTLM handshake.
我需要将 Apache Axis 1.4 连接到使用 NTLM 身份验证来限制对其操作的访问的 Web 服务。我期待使用 Samba Jcifs 来处理 NTLM 握手。
I found
我发现
http://hc.apache.org/httpcomponents-client/ntlm.html
http://hc.apache.org/httpcomponents-client/ntlm.html
which gives me fantastic directions for how to wire up HttpClient 4.0 with jcifs.
这为我提供了如何将 HttpClient 4.0 与 jcifs 连接起来的绝妙方向。
Trouble is, Axis wants to use Http Client 3.0 and the two apis look very different.
麻烦的是,Axis 想使用 Http Client 3.0,这两个 api 看起来很不一样。
There are 2 possibilities that I can see
我可以看到 2 种可能性
- Write an object for Axis that lets it plug into HttpClient 4.
- Figure out how to wire HttpClient 3.0 up with Samba Jcifs.
- 为 Axis 编写一个对象,让它插入 HttpClient 4。
- 弄清楚如何将 HttpClient 3.0 与 Samba Jcifs 连接起来。
Number 1. looks non-trivial, but possible Number 2. I cannot find any encouraging messages on the web describing how to do this.
数字 1. 看起来不平凡,但可能 数字 2. 我在网上找不到任何描述如何做到这一点的鼓舞人心的信息。
My question is: has anyone successfully connected samba jcifs with HttpClient 3.0 ? Has anyone already created an Axis HttpSender object that works with HttpClient 4 ?
我的问题是:有没有人成功地将 samba jcifs 与 HttpClient 3.0 连接起来?有没有人已经创建了一个适用于 HttpClient 4 的 Axis HttpSender 对象?
Is there some better alternative that I have not considered?
有没有我没有考虑过的更好的选择?
回答by Ben Hammond
Finally have a solution to this.
终于有办法解决这个问题了。
The problem
问题
Apache Axis uses Apache HTTPClientwhich provides its own NTLM implementation.
However this implementation is incomplete; it only supports the primitive LM authentication.
The system I need to connect to insists upon the more recent NTLM authentication.
Apache Axis 使用 Apache HTTPClient,它提供了自己的 NTLM 实现。
然而,这个实现是不完整的;它只支持原始LM认证。
我需要连接的系统坚持使用更新的 NTLM 身份验证。
Therefore my Webservice was failing to authenticate when using the Apache HTTP Client with NTLM.
因此,当使用带有 NTLM 的 Apache HTTP 客户端时,我的 Web 服务无法进行身份验证。
This actually then enters an infinite loop as the HTTPClientwill never stop trying and failing to authenticate.
这实际上然后进入了一个无限循环,因为HTTPClient它将永远不会停止尝试和失败的身份验证。
The solution
解决方案
jcifs fully supports all 3 versions of the NTLM handshake.
I have copy-and-pasted org.apache.commons.httpclient.auth.NTLMinto my own class (it is declared as 'final' in order to defeat inheritance)
jcifs 完全支持所有 3 个版本的 NTLM 握手。
我已经复制并粘贴org.apache.commons.httpclient.auth.NTLM到我自己的类中(为了打败继承,它被声明为“最终”)
I have then overwritten the method
然后我覆盖了该方法
public String getType3Message(
String user, String password, String host, String domain,
byte[] nonce) throws AuthenticationException
to construct an instance of jcifs.ntlmssp.Type3Messageand use this object to return a Type3Messagethat has the NTML authentication correctly generated.
构造一个实例jcifs.ntlmssp.Type3Message并使用此对象返回一个Type3Message正确生成了 NTML 身份验证的对象。
I then needed to create my own instance of org.apache.commons.httpclient.auth.AuthSchemeto make use of this new NTLM implementation. call
org.apache.commons.httpclient.auth.AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, MyNewAuthScheme.class)
然后我需要创建我自己的实例org.apache.commons.httpclient.auth.AuthScheme以利用这个新的 NTLM 实现。称呼
org.apache.commons.httpclient.auth.AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, MyNewAuthScheme.class)
start up my WS endpoint stub.
启动我的 WS 端点存根。
And it works !!!
它有效!!!
回答by roman sidler
Thank yo very much Ben, good work. For my solution I need 2 improvements, based on your classes.
非常感谢你本,干得好。对于我的解决方案,我需要根据您的课程进行 2 项改进。
1) class JcifsNtlmScheme
1) 类 JcifsNtlmScheme
The interface has changed in jcifs (I use version 1.3.14). The NTLM flag is required, I'm not really sure but 0x82 works for me.
jcifs 中的界面发生了变化(我使用的是 1.3.14 版)。NTLM 标志是必需的,我不太确定,但 0x82 对我有用。
int flags = Type3Message.NTLMSSP_NEGOTIATE_OEM | Type3Message.NTLMSSP_NEGOTIATE_LM_KEY;
Type3Message msg3 =
new Type3Message(msg2, ntcredentials.getPassword(),
ntcredentials.getDomain(), ntcredentials.getUserName(), ntcredentials.getHost(), flags);
2) class NtlmJcifsCredentials
2)类 NtlmJcifsCredentials
DefaultHttpParams.setHttpParamsFactory(paramFact);
This works fine for the first connection. It seems to be a global setting. It's probably not really thread-safe. I need the credentials on connections base. So I dropped this class and inserted the built-in Authenticator directly after creation of the webservice stub:
这适用于第一次连接。这似乎是一个全局设置。它可能不是真正的线程安全。我需要基于连接的凭据。所以我删除了这个类,并在创建 webservice 存根后直接插入了内置的 Authenticator:
jcifs.Config.setProperty("jcifs.encoding", "ASCII");
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JcifsNtlmScheme.class);
Authenticator authenticator = new Authenticator();
List<String> authScheme = new ArrayList<String>();
authScheme.add(Authenticator.NTLM);
authScheme.add(Authenticator.BASIC);
authenticator.setAuthSchemes(authScheme);
authenticator.setUsername(myusername);
authenticator.setPassword(mypassword);
authenticator.setHost(servername);
authenticator.setDomain(domain);
exService._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);
exService._getServiceClient().getOptions().setProperty(HTTPConstants.CHUNKED, Boolean.FALSE);
exService._getServiceClient().getOptions().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Boolean.TRUE);
回答by LVC
This Axis2Patch.ziphas been a real lifesaver. This is what I did:
这个Axis2Patch.zip是一个真正的救星。这就是我所做的:
Compiled Axis2Patch with httpclient4.1 beta1 which has NTLMv2 build in. Imported that into my project and imported httpclient4.1beta1 also.
使用内置 NTLMv2 的 httpclient4.1 beta1 编译 Axis2Patch。将其导入我的项目并导入 httpclient4.1beta1。
I changed my imports like this:
我像这样改变了我的进口:
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.NTLMScheme;
//import org.apache.commons.httpclient.NTCredentials;
//import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.params.AuthPolicy;
and without too much code change it works perfectly. Thanks!
无需太多代码更改,它就可以完美运行。谢谢!
回答by Ben Hammond
In response to Sergey's comment...
回应谢尔盖的评论......
I have two clases in my solution. An Authorisation Scheme like this
我的解决方案中有两个类。像这样的授权方案
import java.io.IOException;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthChallengeParser;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.InvalidCredentialsException;
import org.apache.commons.httpclient.auth.MalformedChallengeException;
import org.apache.commons.httpclient.auth.NTLMScheme;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* AuthScheme that delegates the work of reading and writing NTLM messages to
* the JCIFS implementation
*
* directly inspired by org.apache.commons.httpclient.auth.NTLMScheme
*
*
* This software is based upon voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*
*/
public class JcifsNtlmScheme implements AuthScheme
{
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(NTLMScheme.class);
/** NTLM challenge string. */
private String ntlmchallenge = null;
private static final int UNINITIATED = 0;
private static final int INITIATED = 1;
private static final int TYPE1_MSG_GENERATED = 2;
private static final int TYPE2_MSG_RECEIVED = 3;
private static final int TYPE3_MSG_GENERATED = 4;
private static final int FAILED = Integer.MAX_VALUE;
/** Authentication process state */
private int state;
/**
* Default constructor for the NTLM authentication scheme.
*
* @since 3.0
*/
public JcifsNtlmScheme()
{
super();
this.state = UNINITIATED;
}
/**
* Constructor for the NTLM authentication scheme.
*
* @param challenge
* The authentication challenge
*
* @throws MalformedChallengeException
* is thrown if the authentication challenge is malformed
*/
public JcifsNtlmScheme(final String challenge)
throws MalformedChallengeException
{
super();
processChallenge(challenge);
}
/**
* Processes the NTLM challenge.
*
* @param challenge
* the challenge string
*
* @throws MalformedChallengeException
* is thrown if the authentication challenge is malformed
*
* @since 3.0
*/
public void processChallenge(final String challenge)
throws MalformedChallengeException
{
String s = AuthChallengeParser.extractScheme(challenge);
if (!s.equalsIgnoreCase(getSchemeName()))
{
throw new MalformedChallengeException("Invalid NTLM challenge: "
+ challenge);
}
int i = challenge.indexOf(' ');
if (i != -1)
{
s = challenge.substring(i, challenge.length());
this.ntlmchallenge = s.trim();
this.state = TYPE2_MSG_RECEIVED;
}
else
{
this.ntlmchallenge = "";
if (this.state == UNINITIATED)
{
this.state = INITIATED;
}
else
{
this.state = FAILED;
}
}
}
/**
* Tests if the NTLM authentication process has been completed.
*
* @return true if Basic authorization has been processed,
* false otherwise.
*
* @since 3.0
*/
public boolean isComplete()
{
return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
}
/**
* Returns textual designation of the NTLM authentication scheme.
*
* @return ntlm
*/
public String getSchemeName()
{
return "ntlm";
}
/**
* The concept of an authentication realm is not supported by the NTLM
* authentication scheme. Always returns null.
*
* @return null
*/
public String getRealm()
{
return null;
}
/**
* Unsupported.
*/
public String getID()
{
throw new UnsupportedOperationException();
}
/**
* Returns the authentication parameter with the given name, if available.
*
*
* There are no valid parameters for NTLM authentication so this method
* always returns null.
*
*
* @param name
* The name of the parameter to be returned
*
* @return the parameter with the given name
*/
public String getParameter(String name)
{
if (name == null)
{
throw new IllegalArgumentException("Parameter name may not be null");
}
return null;
}
/**
* Returns true. NTLM authentication scheme is connection based.
*
* @return true.
*
* @since 3.0
*/
public boolean isConnectionBased()
{
return true;
}
/**
* Unsupported.
*/
public static String authenticate(
final NTCredentials credentials, final String challenge)
throws AuthenticationException
{
throw new UnsupportedOperationException();
}
/**
* Unsupported.
*/
public static String authenticate(
final NTCredentials credentials, final String challenge,
String charset) throws AuthenticationException
{
throw new UnsupportedOperationException();
}
/**
* Unsupported.
*/
public String authenticate(
Credentials credentials, String method, String uri)
throws AuthenticationException
{
throw new UnsupportedOperationException();
}
/**
* Produces NTLM authorization string for the given set of
* {@link Credentials}.
*
* @param credentials
* The set of credentials to be used for athentication
* @param method
* The method being authenticated
*
* @throws InvalidCredentialsException
* if authentication credentials are not valid or not applicable
* for this authentication scheme
* @throws AuthenticationException
* if authorization string cannot be generated due to an
* authentication failure
*
* @return an NTLM authorization string
*
* @since 3.0
*/
public String authenticate(Credentials credentials, HttpMethod method)
throws AuthenticationException
{
LOG.trace("enter NTLMScheme.authenticate(Credentials, HttpMethod)");
if (this.state == UNINITIATED)
{
throw new IllegalStateException(
"NTLM authentication process has not been initiated");
}
NTCredentials ntcredentials = null;
try
{
ntcredentials = (NTCredentials) credentials;
}
catch (ClassCastException e)
{
throw new InvalidCredentialsException(
"Credentials cannot be used for NTLM authentication: "
+ credentials.getClass().getName());
}
byte[] msgBytes = null;
String response = null;
if (this.state == INITIATED)
{
Type1Message msg = new Type1Message();
// @see http://davenport.sourceforge.net/ntlm.html#theType1Message
// dont' support Unicode
// negotiate OEM
// request authentication realm in Type2 response
// not signed
// not encrypted
// not authenticated
// no lan manager key
// negotiate NTLM
msg.setFlags(0x5206);
msg.setSuppliedWorkstation(ntcredentials.getHost());
msg.setSuppliedDomain(ntcredentials.getDomain());
msgBytes = msg.toByteArray();
this.state = TYPE1_MSG_GENERATED;
}
else if (this.state == TYPE2_MSG_RECEIVED)
{
byte[] msg2Bytes =
Base64.decodeBase64(EncodingUtil.getBytes(
this.ntlmchallenge,
method.getParams().getCredentialCharset()));
try
{
Type2Message msg2 = new Type2Message(msg2Bytes);
Type3Message msg3 =
new Type3Message(msg2, ntcredentials.getPassword(),
ntcredentials.getDomain(), ntcredentials
.getUserName(), ntcredentials.getHost());
msgBytes = msg3.toByteArray();
}
catch (IOException ex)
{
throw new AuthenticationException(
"unable to parse Type2Message", ex);
}
this.state = TYPE3_MSG_GENERATED;
}
else
{
throw new RuntimeException("failed to authenticate");
}
response = EncodingUtil.getAsciiString(Base64.encodeBase64(msgBytes));
return "NTLM " + response;
}
}
And a class to register the authorisation scheme, like this
还有一个类来注册授权方案,像这样
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
import org.apache.commons.httpclient.auth.CredentialsProvider;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.DefaultHttpParamsFactory;
import org.apache.commons.httpclient.params.HttpParams;
/**
* registers NTLM authentication for apache axis
*
*/
public class NtlmJcifsCredentials
{
public static void register(String password)
{
final String username = System.getProperty("user.name");
final String computername = System.getenv("COMPUTERNAME");
final String userDomain = System.getenv("USERDOMAIN");
register(username, password, computername, userDomain);
}
public static void register(String username, String password, String userDomain)
{
final String computername = System.getenv("COMPUTERNAME");
register(username, password, computername, userDomain);
}
public static void register(
String username, String password, String computername, String domain)
{
final NTCredentials ntCred =
new NTCredentials(username, password, computername, domain);
final CredentialsProvider ntlmCredProvider = new CredentialsProvider()
{
public Credentials getCredentials(
AuthScheme scheme, String host, int port, boolean proxy)
throws CredentialsNotAvailableException
{
return ntCred;
}
};
final DefaultHttpParamsFactory paramFact =
new DefaultHttpParamsFactory()
{
@Override
protected HttpParams createParams()
{
HttpParams htp = super.createParams();
htp.setParameter(
CredentialsProvider.PROVIDER,
ntlmCredProvider);
return htp;
}
};
DefaultHttpParams.setHttpParamsFactory(paramFact);
// we want all our jcifs encoding to be ascii
jcifs.Config.setProperty("jcifs.encoding", "ASCII");
// our jcifs implemented NTLM is required for MDW's authentication
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JcifsNtlmScheme.class);
}
}
At runtime I call
在运行时我调用
NtlmJcifsCredentials.register(username, password, domain)
I construct my endpoint stub, and it just works. As a beneficial side-effect this will simply chuck an exception if if fails to authenticate - the Default Apache Commons class will keep trying to connect infinitely - which in the case of NTLM can easily lead to your account being locked out from windows.
我构建了我的端点存根,它可以正常工作。作为一个有益的副作用,如果身份验证失败,这将简单地抛出一个异常 - 默认 Apache Commons 类将继续尝试无限连接 - 在 NTLM 的情况下,这很容易导致您的帐户被 Windows 锁定。
回答by Sander Postma
I've got it working, but I did NOT yet implement proxy server support in the HTTP. http://www.magsoft.nl/share/Axis2%20patch.zipAll the jars I use are in the project lib directory. There are some class path requirements. First the Axis2 HTTPClient4 patch.jar has to be above the axis jars. Furthermore, commons-httpclient-3.1.jar needs to be still in the classpath, but after the httpclient-4 jars.
我已经让它工作了,但我还没有在 HTTP 中实现代理服务器支持。 http://www.magsoft.nl/share/Axis2%20patch.zip我用的所有jar包都在项目lib目录下。有一些类路径要求。首先,Axis2 HTTPClient4 patch.jar 必须位于轴 jar 之上。此外,commons-httpclient-3.1.jar 需要仍在类路径中,但在 httpclient-4 jar 之后。
Here is how I implemented the client:
这是我实现客户端的方式:
Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
SchemeRegistry sr = new SchemeRegistry();
sr.register(http);
HttpParams httpParms = new BasicHttpParams();
ClientConnectionManager connManager = new ThreadSafeClientConnManager(httpParms, sr);
DefaultHttpClient httpclient = new DefaultHttpClient(connManager, httpParms);
httpclient.getAuthSchemes().register(HttpTransportProperties.Authenticator.NTLM, new NTLMSchemeFactory());
httpclient.getCredentialsProvider().setCredentials(new AuthScope(host, -1), new NTCredentials(user, pass, host, domain));
sps = new SharepointServiceStub(addr.toString());
List authScheme = new ArrayList();
authScheme.add(HttpTransportProperties.Authenticator.NTLM);
HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setHost(host);
auth.setDomain(domain);
auth.setUsername(user);
auth.setPassword(pass);
auth.setAuthSchemes(authScheme);
Options options = sps._getServiceClient().getOptions();
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.REUSE_HTTP_CLIENT, true);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CACHED_HTTP_CLIENT, httpclient);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.TRUE);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CONNECTION_TIMEOUT, 900000); // 15 minutes
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.SO_TIMEOUT, 180000); // 3 minutes
But for this to work you will need the following tree classes: NTLMSchemeFactory.java
但要使其工作,您将需要以下树类:NTLMSchemeFactory.java
package ...;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.params.HttpParams;
public class NTLMSchemeFactory implements AuthSchemeFactory
{
public NTLMSchemeFactory()
{
}
public AuthScheme newInstance(final HttpParams params)
{
return new NTLMScheme(new JCIFSEngine());
}
}
JCIFSScheme.java
JCIFSScheme.java
package ...;
import org.apache.http.impl.auth.NTLMScheme;
public class JCIFSScheme extends NTLMScheme
{
public JCIFSScheme()
{
super(new JCIFSEngine());
}
}
JCIFSEngine.java
JCIFSEngine.java
package ...;
import java.io.IOException;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;
public class JCIFSEngine implements NTLMEngine
{
public String generateType1Msg(String domain, String workstation) throws NTLMEngineException
{
Type1Message t1m = new Type1Message(Type1Message.getDefaultFlags(), domain, workstation);
return Base64.encode(t1m.toByteArray());
}
public String generateType3Msg(String username, String password, String domain, String workstation, String challenge)
throws NTLMEngineException
{
Type2Message t2m;
try
{
t2m = new Type2Message(Base64.decode(challenge));
} catch (IOException ex)
{
throw new NTLMEngineException("Invalid Type2 message", ex);
}
Type3Message t3m = new Type3Message(t2m, password, domain, username, workstation, 0);
return Base64.encode(t3m.toByteArray());
}
}

