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

3pvhb19x  于 2022-11-15  发布在  Apache
关注(0)|答案(1)|浏览(259)

我正在调用在IIS服务器下远程运行的.net编写的Web服务。
我用Eclipse IDE使用apache axis 1.4创建了它的存根,并创建了一个Web服务客户端。这只是一个测试客户端,实际上它将是我的Web应用程序调用这个Web服务。
我们保留了它两个不同的端点,用于保持安全凭据的启用/禁用。
1.“ip:端口/鸽子/鸽子.svc;“//身份验证已禁用
1.“IP:端口/pwa/鸽子.svc;//启用身份验证
因此,现在当我使用端点no(1)时,我可以调用Web服务并完成任务,但由于我希望强制应用安全凭据,因此当我使用端点no(2)时,我会遇到以下异常
(401)未经授权
(401)未经授权的轴故障
错误代码:{http://xml.apache.org/axis/}HTTP
故障子代码:
错误字符串:(401)未经授权
故障执行元:
故障节点:
故障详细信息:{}:返回代码:401
我要传递此格式凭据:
1)域\用户名
2)口令
我试着在这里添加其他帖子的建议,说在存根中设置相应的调用前方法,但我得到了上面提到的相同的异常。
(mystub)._setProperty(javax.xml.rpc.存根.用户名_属性,域+“\”+用户名);
(我的存根)._setProperty(javax.xml.rpc.存根.密码_属性,密码);

然而,通过一些搜索,现在我能够通过调用我远程.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客户端来实现,因为存根是用我的web服务客户端中的.net web服务提供的wsdl生成的。我尝试在invoke()调用之前通过根据上面的演示进行修改来更改@stub level,但是它抛出了相同的未经授权的异常。
这只是对使用NTLM身份验证技术远程IIS服务器的一个参考。
需要有关使用java进行Windows身份验证的帮助,以便将安全凭据传递到IIS。

[注意:我的axis客户端(java)传递domain\user和密码,这是在另一端正确配置IIS服务器的]

7gcisfzg

7gcisfzg1#

问题是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)

在谷歌搜索,问题是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);

Thanks to Sachin's Tech Place

相关问题