我正在调用一个用.net编写的web服务,该服务位于IIS服务器下远程运行。
我使用带有eclipseIDE的apache axis 1.4
创建了它各自的存根,并创建了各自的web服务客户端。这只是一个测试客户端,实际上它将是我的web应用程序来调用这个web服务。
我们保留了它的两个不同端点,用于保持安全凭据的启用/禁用。
- "ip:port/divege/diveg.svc;"//身份验证已禁用
-
"ip:pwa/pgeon.svc;//已启用身份验证的
所以现在,当我使用端点no(1)时,我可以调用web服务并完成任务,但由于我想强制应用安全凭据,所以当我使用终结点no(2)时,异常以下
(401)未经授权
(401)未经授权的AxisFault
故障代码:{http://xml.apache.org/axis/}HTTP
fault子代码:
faultString:(401)未经授权
faultActor:
faultNode:
faultDetail:{}:返回代码:401
我想通过以下格式的凭据:
1) 域\用户名
2) 密码
我试着在这里添加其他帖子的建议,其中说在存根中设置相应的before-call方法,但我得到了上面提到的相同的异常。
(mystub)_setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY,域+"\"+用户名)
(mystub)_setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY,密码);
H然而,通过一些搜索,我现在可以用java独立程序进行NTML身份验证,通过这样做调用我的远程.net web服务:
public static void main(String[] args) throws Exception {
String urlStr = “http://example.com/root/action.dll?p1=value1″;
String domain = “”; // May also be referred as realm
String userName = “CHANGE_ME”;
String password = “CHANGE_ME”;
String responseText = getAuthenticatedResponse(urlStr, domain, userName, password);
System.out.println(”response: ” + responseText);
}
private static String getAuthenticatedResponse(final String urlStr, final String domain, final String userName, final String password) throws IOException {
StringBuilder response = new StringBuilder();
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(domain + “\” + userName, password.toCharArray());
}
});
URL urlRequest = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod(”GET”);
InputStream stream = conn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String str = “”;
while ((str = in.readLine()) != null) {
response.append(str);
}
in.close();
return response.toString();
}
但是我不能用axis客户端来实现这一点,因为存根是用我的web服务客户端中的.net web服务提供的wsdl生成的。我试着在invoke()调用之前更改@stub级别,根据上面的演示进行修改,但它抛出了同样的未经授权的异常。
这只是所有使用NTLM身份验证技术的远程IIS服务器。
需要使用java进行windows身份验证以将安全凭据传递给IIS的帮助。
[注意:我的axis客户端(java)用密码传递域\用户,这是用另一端的IIS服务器正确配置的]
问题是Axis 1.4没有正确实现NTLM V2协议。
我遇到了Sharepoint 2010 web服务的问题。我有一个在Windows2003服务器上运行的Sharepoint2007的客户端。然后,我用运行在Windows 2008 R2服务器上的Sharepoint 2010 web服务测试了这个客户端,它们停止了工作。错误是:
Caused by: (401)Unauthorized
at org.apache.axis.transport.http.CommonsHTTPSender.invoke(CommonsHTTPSender.java:218)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
在谷歌中搜索,问题是Windows 2003使用NTLM V1协议作为默认协议,而Windows 2008 R2使用NTLM V2作为默认协议。
我在以下网址中找到了解决方案和问题的完美解释:
http://devsac.blogspot.com.es/2010/10/supoprt-for-ntlmv2-with-apache.html
解决方案是创建以下类来解析HttpClient3.x:
public class JCIFS_NTLMScheme implements AuthScheme {
private static AppLogger logger = new AppLogger(HTTPHelper.class.getName());
/** 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 {
logger.doLog(AppLogger.FINEST,
"Enter JCIFS_NTLMScheme.authenticate(Credentials, HttpMethod)",
null);
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.
*
* <p>
* There are no valid parameters for NTLM authentication so this method
* always returns <tt>null</tt>.
* </p>
*
* @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 <code>null</code>.
*
* @return <code>null</code>
*/
public String getRealm() {
return null;
}
/**
* Returns textual designation of the NTLM authentication scheme.
*
* @return <code>ntlm</code>
*/
public String getSchemeName() {
return "ntlm";
}
/**
* Tests if the NTLM authentication process has been completed.
*
* @return <tt>true</tt> if Basic authorization has been processed,
* <tt>false</tt> otherwise.
*
* @since 3.0
*/
public boolean isComplete() {
return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
}
/**
* Returns <tt>true</tt>. NTLM authentication scheme is connection based.
*
* @return <tt>true</tt>.
*
* @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());
}
}
}
然后是使用以下命令注册新的JCIFS_NTLMScheme类作为NTLMScheme:
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, org.xyz.JCIFS_NTLMScheme.class);
感谢Sachin的Tech Place