401:apache axis客户端(java)使用NTLM身份验证技术调用Web服务(.net)时发生未经授权的异常



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

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

我们保留了它的两个不同端点,用于保持安全凭据的启用/禁用。

  1. "ip:port/divege/diveg.svc;"//身份验证已禁用
  2. "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

最新更新