我正在调用一个在 IIS 服务器下远程运行的 .net 中编写的 Web 服务。
我使用 apache axis 1.4
和 eclipse IDE 创建了各自的 stub ,并创建了各自的 web 服务客户端。这实际上只是一个测试客户端,我的 Web 应用程序将调用此 Web 服务。
我们保留了其两个不同的端点以保持安全凭证启用/禁用。
- “ip:port/pigeon/pigeon.svc;”//身份验证已禁用
"ip:port/pwa/pigeon.svc;//启用身份验证
所以现在当我使用端点号 (1) 时,我可以调用 Web 服务并完成工作,但由于我想强制应用安全凭证,所以当我使用端点号 (2) 时,我遇到了以下异常
(401)Unauthorized
(401)Unauthorized AxisFault
faultCode: {http://xml.apache.org/axis/}HTTP
faultSubcode:
faultString: (401)Unauthorized
faultActor:
faultNode:
faultDetail: {}:return code: 401
我想传递这种格式的凭据:
1) 域\用户名
2)密码
我尝试在此处添加其他帖子的建议,其中说在 stub 中设置相应的调用前方法,但我得到了与上述相同的异常。
(mystub)._setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY, domain + "\" + username);
(mystub)._setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY, password);
然而,通过一些搜索,现在我可以通过调用我的远程 .net Web 服务的 java 独立程序进行 NTML 身份验证:
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 客户端执行此操作,因为 stub 是使用我的 Web 服务客户端中的 .net Web 服务提供的 wsdl 生成的。我尝试根据上面的演示通过修改在 invoke() 调用之前更改 @stub 级别,但它会抛出相同的未经授权的异常。
这只是 fyi/所有使用 NTLM 身份验证技术的远程 IIS 服务器。
希望在使用 java 的 Windows 身份验证上提供帮助,以便将安全凭证传递给 IIS。
[注意:我的 axis 客户端 (java) 通过域\用户和密码,这是在另一端正确配置的 IIS 服务器]
最佳答案
问题是 Axis 1.4 没有正确实现 NTLM V2 协议(protocol)。
我遇到了 Sharepoint 2010 网络服务的问题。我有一个客户端可以与在 Windows 2003 服务器上运行的 Sharepoint 2007 完美配合。然后,我使用在 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协议(protocol),而Windows 2008 R2默认使用NTLM V2。
我在以下网址中找到了解决方案和问题的完美解释:
http://devsac.blogspot.com.es/2010/10/supoprt-for-ntlmv2-with-apache.html
解决方案是创建以下类来解析 HttpClient 3.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);
关于java - 401 : Unauthorized Exception occurred with an apache axis client(java) to invoke a webservice(. 网络)使用 NTLM 身份验证技术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17855960/