2

我正在调用一个用 .net 编写的 Web 服务,它位于 IIS 服务器下远程运行。

apache axis 1.4我使用Eclipse IDE创建了它各自的存根,并创建了一个各自的 Web 服务客户端。这实际上只是一个测试客户端,它将是我的 Web 应用程序将调用此 Web 服务。

我们保留了两个不同的端点来保持安全凭证启用/禁用。

  1. “IP:端口/鸽子/pigeon.svc;” //身份验证禁用
  2. "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)密码

我尝试在此处添加其他帖子的建议,其中说在存根中设置相应的调用前方法,但我得到了与上述相同的异常。

(mystub)._setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY, domain + "\" + username);
(mystub)._setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY, 密码);

但是现在通过一些搜索,我可以使用 java 独立程序通过执行以下操作调用我的远程 .net Web 服务来进行 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();
}

但是我无法使用我的轴客户端执行此操作,因为存根是使用我的 Web 服务客户端中的 .net Web 服务提供的 wsdl 生成的。我尝试通过根据上面的演示修改调用()调用之前更改@stub级别,但它会引发相同的未经授权的异常。

这只是使用 NTLM 身份验证技术的远程 IIS 服务器。

使用 java 进行 Windows 身份验证的帮助,以便将安全凭证传递给 IIS。

[注意:我的轴客户端(java)使用密码传递域\用户,这是正确配置另一端的IIS服务器]

4

1 回答 1

0

问题是 Axis 1.4 没有正确实现 NTLM V2 协议。

我遇到了 Sharepoint 2010 Web 服务的问题。我有一个客户端与在 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)

在google中搜索,问题是Windows 2003默认使用NTLM V1协议,而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);

感谢 Sachin 的 Tech Place

于 2014-10-03T15:01:40.767 回答