简体   繁体   English

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

[英]401: Unauthorized Exception occurred with an apache axis client(java) to invoke a webservice(.net) with an NTLM Authentication Technique

I am calling a web service written in .net located remotely running under IIS Server.我正在调用一个用 .net 编写的 Web 服务,该服务位于远程运行在 IIS 服务器下。

I created its respective stub using apache axis 1.4 with eclipse IDE and created a respective web service client.我使用apache axis 1.4和 Eclipse IDE 创建了各自的存根,并创建了各自的 Web 服务客户端。 This is just a test client in actual it will be my web application going to call this web service.这实际上只是一个测试客户端,它将是我的 Web 应用程序将调用此 Web 服务。

We have kept its two different endpoint for keeping security credential enable/disable.我们保留了它的两个不同端点,用于保持安全凭证启用/禁用。

  1. "ip:port/pigeon/pigeon.svc;" "ip:port/pigeon/pigeon.svc;" // authentication disabled // 认证失效
  2. "ip:port/pwa/pigeon.svc; // authentiction enabled "ip:port/pwa/pigeon.svc; // 认证开启

    So now when I am using endpoint no (1) I am able to call web service and gets things done, but since I want to apply security credential mandatory so when I use endpoint no (2) I am getting below exception所以现在当我使用端点号 (1) 时,我可以调用 Web 服务并完成任务,但是由于我想强制应用安全凭证,所以当我使用端点号 (2) 时,我遇到了异常

(401)Unauthorized (401)未经授权
(401)Unauthorized AxisFault (401)未经授权的AxisFault
faultCode: { http://xml.apache.org/axis/ }HTTP故障代码:{ http://xml.apache.org/axis/}HTTP
faultSubcode:故障子代码:
faultString: (401)Unauthorized故障字符串:(401)未经授权
faultActor:故障演员:
faultNode:故障节点:
faultDetail: {}:return code: 401故障详细信息:{}:返回代码:401

I want to pass credential which are in this format :我想传递这种格式的凭证:
1) domain\\username 1) 域\\用户名
2) password 2) 密码

I tried adding suggestion of other post over here which says set the respective before call method in stub but I am getting the same above mentioned exception.我尝试在此处添加其他帖子的建议,其中说在存根中设置相应的 before call 方法,但我遇到了与上述相同的异常。

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

Hhowever with some search now I am able to do NTML authentication with java stand alone program invoking my remote .net web service by doing this :但是,现在通过一些搜索,我可以使用 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();
}

But I am not able to do so with my axis client as stub are generated with wsdl provided by .net web service in my web service client.但是我不能用我的轴客户端这样做,因为存根是用我的 web 服务客户端中的 .net web 服务提供的 wsdl 生成的。 I tried changing @stub level before invoke() call by modifying according to above demo but it throws same unauthorized exception.我尝试通过根据上面的演示进行修改来在 invoke() 调用之前更改 @stub 级别,但它引发了相同的未授权异常。

This is just fyi/all that remote IIS server using NTLM authentication technique.这只是仅供参考/所有使用 NTLM 身份验证技术的远程 IIS 服务器。

Help expected on windows authentication using java in order to pass security credential to IIS.使用 java 进行 Windows 身份验证的帮助,以便将安全凭证传递给 IIS。

[Note : my axis client(java) passes domain\\user with password ,this is configured with IIS server on the otherside properly] [注意:我的轴客户端(java)通过密码传递域\\用户,这是在另一端正确配置了IIS服务器]

The problem is that Axis 1.4 does not implement correctly the NTLM V2 protocol.问题是 Axis 1.4 没有正确实现 NTLM V2 协议。

I experienced the problem with Sharepoint 2010 web services.我遇到了 Sharepoint 2010 Web 服务的问题。 I had a client working perfectly with Sharepoint 2007 running on Windows 2003 server.我有一个客户端与在 Windows 2003 服务器上运行的 Sharepoint 2007 完美配合。 Then, I tested this client with Sharepoint 2010 web services running on Windows 2008 R2 server and they stop working.然后,我使用在 Windows 2008 R2 服务器上运行的 Sharepoint 2010 Web 服务测试了该客户端,但它们停止工作。 The error was:错误是:

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)

Searching in google, the problem was that Windows 2003 is using NTLM V1 protocol as default, while Windows 2008 R2 was using NTLM V2 as default.在谷歌搜索,问题是Windows 2003默认使用NTLM V1协议,而Windows 2008 R2默认使用NTLM V2。

I found the solution and the problem explained perfectly in the following url:我找到了解决方案,问题在以下网址中得到了完美的解释:

http://devsac.blogspot.com.es/2010/10/supoprt-for-ntlmv2-with-apache.html http://devsac.blogspot.com.es/2010/10/supoprt-for-ntlmv2-with-apache.html

The solution is creating the following class to resolve the HttpClient 3.x:解决方案是创建以下类来解析 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());

          }

   }

} }

Then it was Register the new JCIFS_NTLMScheme class as the replacement for NTLMScheme by using the following command:然后使用以下命令将新的 JCIFS_NTLMScheme 类注册为 NTLMScheme 的替代品:

AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, org.xyz.JCIFS_NTLMScheme.class);

Thanks to Sachin's Tech Place感谢 Sachin 的 Tech Place

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM