搞了两天的AXIS2连接带ntlm认证的web service,要吐血了,百度一堆,不是故作神秘的讲一大堆,就是代码给一半,装吧。。。。
国人就是,知道的不愿意说,说也只说一半,不知道的又装知道,真是气死了。
不得已,问google,不是崇洋媚外,确实国外的程序员比较专业,问的问题无不知无不言的,没有只说一半的,所有的代码,所有的连接全部列得清清楚楚,生怕你做错,所有都罗列的清清楚楚,这个才是真正的技术分享!!!气死我了!不说了,开始分享我的axis2的ntlm认证。
本例是以调用Milestone的web service为例:
1,首先,下载axis2的包,把需要的jar放进自己的project,以下图片显示需要的lib
因为其web services是c#编写,调用需要ntlm认证:
2,用axis2的wsdl2java生成它的class(网上一大把,这里不说了),把生成的java文件放进自己的project。
3,需要用到一个关键的class:JCIFS_NTLMScheme.java
详情链接:http://devsac.blogspot.sg/2010/10/supoprt-for-ntlmv2-with-apache.html
里面有解释和源码,直接copy然后保存成JCIFS_NTLMScheme.java即可。
4,一切都准备好了,开始码code了
代码片段:
//change the authpolicy use the JCIFS_NTLMScheme.class---httpclient
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JCIFS_NTLMScheme.class);
//create basicAuth
Authenticator basicAuth = new HttpTransportProperties.Authenticator();
basicAuth.setDomain(".");
basicAuth.setHost("192.168.60.204");
basicAuth.setUsername("administrator");
basicAuth.setPassword("password,1");
basicAuth.setPreemptiveAuthentication(false);
//end create basicAuth
//create webservices client instance
ServerCommandServiceStub scs=new ServerCommandServiceStub();
//Not need this..scs._getServiceClient().getOptions().setUserName("administrator");
//Not need this..scs._getServiceClient().getOptions().setPassword("password,1");
//create EndpointReference
//this url 'http://192.168.60.204/ServerAPI/ServerCommandService.asmx?wsdl' is the webservie url that u want to conn
EndpointReference targetEPR = new EndpointReference("http://192.168.60.204/ServerAPI/ServerCommandService.asmx?wsdl");
//use setTo set the target to client instance's options
scs._getServiceClient().getOptions().setTo(targetEPR);
//use setProperty set the basicAuth that u created just now for authentiction
scs._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, basicAuth);
//*********>>>begin to call the function
/***************call login function in web service*******************/
Login log=new Login();
UUID uid = new UUID(0, 0);
Guid g=new Guid();
String testuid=uid.randomUUID().toString();
System.out.println(testuid);
g.setGuid(testuid);
log.setInstanceId(g);
LoginResponse lr=scs.login(log);
System.out.println(lr.getLoginResult().getToken());
其中用到JCIFS_NTLMScheme.java,setHost,setDomain,setPassword。。。。等等根据你自己的webservice讯息填入。
project可在我上传的资源中下载,包括所有的java和jar文件
最后奉上JCIFS_NTLMScheme.java的代码,以防个别需要而下载不了(copy一下的代码存成JCIFS_NTLMScheme.java即可):
package com.mediaCommon;
import java.io.IOException;
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;
/**
* This is a reimplementation of HTTPClient 3.x's
* org.apache.commons.httpclient.auth.NTLMScheme.
* It will basically use JCIFS (v1.3.15) in order to provide added support for
* NTLMv2 (instead of trying to create its own Type, 2 and 3 messages).
* This class has to be registered manually with HTTPClient before setting
* NTCredentials: AuthPolicy.registerAuthScheme(AuthPolicy.NTLM,
* JCIFS_NTLMScheme.class);
* Will not work with HttpClient 4.x which requires AuthEngine to be overriden instead of AuthScheme.
*
* @author Sachin M
*/
public class JCIFS_NTLMScheme implements AuthScheme {
/** 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;
public JCIFS_NTLMScheme() throws AuthenticationException {
// Check if JCIFS is present. If not present, do not proceed.
try {
Class.forName("jcifs.ntlmssp.NtlmMessage",false,this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new AuthenticationException("Unable to proceed as JCIFS library is not found.");
}
}
public String authenticate(Credentials credentials, HttpMethod method)
throws AuthenticationException {
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());
}
NTLM ntlm = new NTLM();
ntlm.setCredentialCharset(method.getParams().getCredentialCharset());
String response = null;
if (this.state == INITIATED || this.state == FAILED) {
response = ntlm.generateType1Msg(ntcredentials.getHost(),
ntcredentials.getDomain());
this.state = TYPE1_MSG_GENERATED;
} else {
response = ntlm.generateType3Msg(ntcredentials.getUserName(),
ntcredentials.getPassword(), ntcredentials.getHost(),
ntcredentials.getDomain(), this.ntlmchallenge);
this.state = TYPE3_MSG_GENERATED;
}
return "NTLM " + response;
}
public String authenticate(Credentials credentials, String method,
String uri) throws AuthenticationException {
throw new RuntimeException(
"Not implemented as it is deprecated anyway in Httpclient 3.x");
}
public String getID() {
throw new RuntimeException(
"Not implemented as it is deprecated anyway in Httpclient 3.x");
}
/**
* 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;
}
/**
* The concept of an authentication realm is not supported by the NTLM
* authentication scheme. Always returns null
.
*
* @return null
*/
public String getRealm() {
return null;
}
/**
* Returns textual designation of the NTLM authentication scheme.
*
* @return ntlm
*/
public String getSchemeName() {
return "ntlm";
}
/**
* 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 true. NTLM authentication scheme is connection based.
*
* @return true.
*
* @since 3.0
*/
public boolean isConnectionBased() {
return true;
}
/**
* 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;
}
}
}
private class NTLM {
/** Character encoding */
public static final String DEFAULT_CHARSET = "ASCII";
/**
* The character was used by 3.x's NTLM to encode the username and
* password. Apparently, this is not needed in when passing username,
* password from NTCredentials to the JCIFS library
*/
private String credentialCharset = DEFAULT_CHARSET;
void setCredentialCharset(String credentialCharset) {
this.credentialCharset = credentialCharset;
}
private String generateType1Msg(String host, String domain) {
jcifs.ntlmssp.Type1Message t1m = new jcifs.ntlmssp.Type1Message(jcifs.ntlmssp.Type1Message.getDefaultFlags(),
domain, host);
return jcifs.util.Base64.encode(t1m.toByteArray());
}
private String generateType3Msg(String username, String password, String host,
String domain, String challenge) {
jcifs.ntlmssp.Type2Message t2m;
try {
t2m = new jcifs.ntlmssp.Type2Message(jcifs.util.Base64.decode(challenge));
} catch (IOException e) {
throw new RuntimeException("Invalid Type2 message", e);
}
jcifs.ntlmssp.Type3Message t3m = new jcifs.ntlmssp.Type3Message(t2m, password, domain,
username, host, 0);
return jcifs.util.Base64.encode(t3m.toByteArray());
}
}
}