【问题标题】:How can I get jcifs to play nicely with apache axis如何让 jcifs 与 apache 轴很好地配合使用
【发布时间】:2010-10-29 08:16:30
【问题描述】:

我需要将 Apache Axis 1.4 连接到使用 NTLM 身份验证来限制对其操作的访问的 Web 服务。 我期待使用 Samba Jcifs 来处理 NTLM 握手。

我找到了

http://hc.apache.org/httpcomponents-client/ntlm.html

这为我提供了如何将 HttpClient 4.0 与 jcifs 连接起来的绝佳指导。

麻烦的是,Axis要使用Http Client 3.0,两个api看起来很不一样。

我可以看到两种可能性

  1. 为 Axis 编写一个对象,让它插入 HttpClient 4。
  2. 弄清楚如何将 HttpClient 3.0 与 Samba Jcifs 连接起来。

数字 1. 看起来不平凡,但可能 第 2 号。我在网上找不到任何令人鼓舞的消息来描述如何做到这一点。

我的问题是:有没有人成功地将 samba jcifs 与 HttpClient 3.0 连接起来? 是否有人已经创建了与 HttpClient 4 一起使用的 Axis HttpSender 对象?

有没有我没有考虑过的更好的选择?

【问题讨论】:

  • 嗯? Jcifs 是针对 CIFS 文件系统的,对吧?您只想要身份验证部分吗?但是 Apache Commons 的 HttpClient 包含 NTLM 身份验证。 ???什么是“SQL 端点”?你到底想做什么?
  • 我需要访问一个使用 NTLM 握手来控制对其操作的访问的 Web 服务。由于知识产权问题,我认为 HttpClient 不提供 NTLM 支持。我相信我可以使用 Samba Jcifs 来创建 NTLM Type 1 消息和 Type3 消息。它是一个隐藏在 Web 服务后面的 SQL 数据库这一事实是一个无关紧要的细节。

标签: java axis2 ntlm


【解决方案1】:

终于有办法解决了。

问题

Apache Axis 使用 Apache HTTPClient,它提供了自己的 NTLM 实现。
然而这个实现是不完整的;它只支持原始的LM认证。
我需要连接的系统坚持使用最新的 NTLM 身份验证。

因此,当使用带有 NTLM 的 Apache HTTP 客户端时,我的 Web 服务无法进行身份验证。

这实际上进入了一个无限循环,因为HTTPClient 将永远不会停止尝试并且无法进行身份验证。

解决办法

jcifs 完全支持 NTLM 握手的所有 3 个版本。
我已将org.apache.commons.httpclient.auth.NTLM 复制并粘贴到我自己的类中(它被声明为“最终”以阻止继承)

然后我已经覆盖了方法

public String getType3Message(
        String user, String password, String host, String domain,
        byte[] nonce) throws AuthenticationException

构造jcifs.ntlmssp.Type3Message的实例 并使用此对象返回正确生成 NTML 身份验证的Type3Message

然后我需要创建自己的org.apache.commons.httpclient.auth.AuthScheme 实例 利用这个新的 NTLM 实现。称呼 org.apache.commons.httpclient.auth.AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, MyNewAuthScheme.class)

启动我的 WS 端点存根。

而且有效!!!

【讨论】:

  • 很好的解决方案,但它有一个缺点。 AuthPolicy.registerAuthScheme() 将向 NTLM 注册 MyNewAuthScheme,并且未来对 NTLM 的任何请求都将使用此方案,而不是来自 apache commons httpclient 的原始 NTLMScheme。如果您打算将代码修复范围缩小到软件中的特定功能,这可能是一个问题。
  • 您可以将整个代码粘贴到某处吗?因为我现在正在努力解决完全相同的问题。
【解决方案2】:

回应谢尔盖的评论...

我的解决方案中有两个类。像这样的授权方案


import java.io.IOException;

import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthChallengeParser;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.InvalidCredentialsException;
import org.apache.commons.httpclient.auth.MalformedChallengeException;
import org.apache.commons.httpclient.auth.NTLMScheme;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * AuthScheme that delegates the work of reading and writing NTLM messages to
 * the JCIFS implementation
 * 
 * directly inspired by org.apache.commons.httpclient.auth.NTLMScheme
 * 
 *
 * This software is based upon voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * .
 *
 */
public class JcifsNtlmScheme implements AuthScheme
{
    /** Log object for this class. */
    private static final Log LOG = LogFactory.getLog(NTLMScheme.class);

    /** 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;

    /**
     * Default constructor for the NTLM authentication scheme.
     * 
     * @since 3.0
     */
    public JcifsNtlmScheme()
    {
        super();
        this.state = UNINITIATED;
    }

    /**
     * Constructor for the NTLM authentication scheme.
     * 
     * @param challenge
     *            The authentication challenge
     * 
     * @throws MalformedChallengeException
     *             is thrown if the authentication challenge is malformed
     */
    public JcifsNtlmScheme(final String challenge)
            throws MalformedChallengeException
    {
        super();
        processChallenge(challenge);
    }

    /**
     * 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;
            }
        }
    }

    /**
     * Tests if the NTLM authentication process has been completed.
     * 
     * @return true if Basic authorization has been processed,
     *         false otherwise.
     * 
     * @since 3.0
     */
    public boolean isComplete()
    {
        return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
    }

    /**
     * Returns textual designation of the NTLM authentication scheme.
     * 
     * @return ntlm
     */
    public String getSchemeName()
    {
        return "ntlm";
    }

    /**
     * The concept of an authentication realm is not supported by the NTLM
     * authentication scheme. Always returns null.
     * 
     * @return null
     */
    public String getRealm()
    {
        return null;
    }

    /**
     * Unsupported.
     */
    public String getID()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns the authentication parameter with the given name, if available.
     * 
     * 

* There are no valid parameters for NTLM authentication so this method * always returns null. *

* * @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; } /** * Returns true. NTLM authentication scheme is connection based. * * @return true. * * @since 3.0 */ public boolean isConnectionBased() { return true; } /** * Unsupported. */ public static String authenticate( final NTCredentials credentials, final String challenge) throws AuthenticationException { throw new UnsupportedOperationException(); } /** * Unsupported. */ public static String authenticate( final NTCredentials credentials, final String challenge, String charset) throws AuthenticationException { throw new UnsupportedOperationException(); } /** * Unsupported. */ public String authenticate( Credentials credentials, String method, String uri) throws AuthenticationException { throw new UnsupportedOperationException(); } /** * Produces NTLM authorization string for the given set of * {@link Credentials}. * * @param credentials * The set of credentials to be used for athentication * @param method * The method being authenticated * * @throws InvalidCredentialsException * if authentication credentials are not valid or not applicable * for this authentication scheme * @throws AuthenticationException * if authorization string cannot be generated due to an * authentication failure * * @return an NTLM authorization string * * @since 3.0 */ public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException { LOG.trace("enter NTLMScheme.authenticate(Credentials, HttpMethod)"); 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()); } byte[] msgBytes = null; String response = null; if (this.state == INITIATED) { Type1Message msg = new Type1Message(); // @see http://davenport.sourceforge.net/ntlm.html#theType1Message // dont' support Unicode // negotiate OEM // request authentication realm in Type2 response // not signed // not encrypted // not authenticated // no lan manager key // negotiate NTLM msg.setFlags(0x5206); msg.setSuppliedWorkstation(ntcredentials.getHost()); msg.setSuppliedDomain(ntcredentials.getDomain()); msgBytes = msg.toByteArray(); this.state = TYPE1_MSG_GENERATED; } else if (this.state == TYPE2_MSG_RECEIVED) { byte[] msg2Bytes = Base64.decodeBase64(EncodingUtil.getBytes( this.ntlmchallenge, method.getParams().getCredentialCharset())); try { Type2Message msg2 = new Type2Message(msg2Bytes); Type3Message msg3 = new Type3Message(msg2, ntcredentials.getPassword(), ntcredentials.getDomain(), ntcredentials .getUserName(), ntcredentials.getHost()); msgBytes = msg3.toByteArray(); } catch (IOException ex) { throw new AuthenticationException( "unable to parse Type2Message", ex); } this.state = TYPE3_MSG_GENERATED; } else { throw new RuntimeException("failed to authenticate"); } response = EncodingUtil.getAsciiString(Base64.encodeBase64(msgBytes)); return "NTLM " + response; } }

还有一个注册授权方案的类,像这样


import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
import org.apache.commons.httpclient.auth.CredentialsProvider;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.DefaultHttpParamsFactory;
import org.apache.commons.httpclient.params.HttpParams;

/**
 * registers NTLM authentication for apache axis
 * 
 */
public class NtlmJcifsCredentials
{
    public static void register(String password)
    {
        final String username = System.getProperty("user.name");
        final String computername = System.getenv("COMPUTERNAME");
        final String userDomain = System.getenv("USERDOMAIN");
        register(username, password, computername, userDomain);
    }
    public static void register(String username, String password, String userDomain)
    {
        final String computername = System.getenv("COMPUTERNAME");
        register(username, password, computername, userDomain);
    }

    public static void register(
            String username, String password, String computername, String domain)
    {
        final NTCredentials ntCred =
                new NTCredentials(username, password, computername, domain);

        final CredentialsProvider ntlmCredProvider = new CredentialsProvider()
        {
            public Credentials getCredentials(
                    AuthScheme scheme, String host, int port, boolean proxy)
                    throws CredentialsNotAvailableException
            {
                return ntCred;
            }
        };
        final DefaultHttpParamsFactory paramFact =
                new DefaultHttpParamsFactory()
                {
                    @Override
                    protected HttpParams createParams()
                    {
                        HttpParams htp = super.createParams();
                        htp.setParameter(
                                CredentialsProvider.PROVIDER,
                                ntlmCredProvider);
                        return htp;
                    }
                };
        DefaultHttpParams.setHttpParamsFactory(paramFact);

        // we want all our jcifs encoding to be ascii
        jcifs.Config.setProperty("jcifs.encoding", "ASCII");

        // our jcifs implemented NTLM is required for MDW's authentication
        AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JcifsNtlmScheme.class);
    }
}

在运行时我调用

 NtlmJcifsCredentials.register(username, password, domain) 

我构建了我的端点存根,它就可以工作。 作为一个有益的副作用,如果无法通过身份验证,这将简单地抛出一个异常 - 默认 Apache Commons 类将继续尝试无限连接 - 在 NTLM 的情况下,这很容易导致您的帐户被 Windows 锁定。

【讨论】:

    【解决方案3】:

    我已经让它工作了,但我还没有在 HTTP 中实现代理服务器支持。 http://www.magsoft.nl/share/Axis2%20patch.zip 我使用的所有 jar 都在项目 lib 目录中。 有一些类路径要求。首先,Axis2 HTTPClient4 patch.jar 必须位于轴 jar 之上。此外,commons-httpclient-3.1.jar 需要仍在类路径中,但在 httpclient-4 jar 之后。

    这是我实现客户端的方式:

    
    Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
    SchemeRegistry sr = new SchemeRegistry();
    sr.register(http);
    HttpParams httpParms = new BasicHttpParams();
    ClientConnectionManager connManager = new ThreadSafeClientConnManager(httpParms, sr);
    DefaultHttpClient httpclient = new DefaultHttpClient(connManager, httpParms);
    httpclient.getAuthSchemes().register(HttpTransportProperties.Authenticator.NTLM, new NTLMSchemeFactory());
    httpclient.getCredentialsProvider().setCredentials(new AuthScope(host, -1), new NTCredentials(user, pass, host, domain));
    
    sps = new SharepointServiceStub(addr.toString());
    
    List authScheme = new ArrayList();
    authScheme.add(HttpTransportProperties.Authenticator.NTLM);
    HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
    auth.setHost(host);
    auth.setDomain(domain);
    auth.setUsername(user);
    auth.setPassword(pass);
    auth.setAuthSchemes(authScheme);
    Options options = sps._getServiceClient().getOptions();
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.REUSE_HTTP_CLIENT, true);
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CACHED_HTTP_CLIENT, httpclient);
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.TRUE);
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CONNECTION_TIMEOUT, 900000); // 15 minutes
    options.setProperty(org.apache.axis2.transport.http.HTTPConstants.SO_TIMEOUT, 180000); // 3 minutes
    

    但要使其工作,您将需要以下树类: NTLMSchemeFactory.java

    
    package ...;
    
    import org.apache.http.auth.AuthScheme;
    import org.apache.http.auth.AuthSchemeFactory;
    import org.apache.http.impl.auth.NTLMScheme;
    import org.apache.http.params.HttpParams;
    
    public class NTLMSchemeFactory implements AuthSchemeFactory
    {
    
        public NTLMSchemeFactory()
        {
        }
    
        public AuthScheme newInstance(final HttpParams params)
        {
            return new NTLMScheme(new JCIFSEngine());
        }
    
    }
    

    JCIFSScheme.java

    
    package ...;
    
    import org.apache.http.impl.auth.NTLMScheme;
    
    
    
    public class JCIFSScheme extends NTLMScheme
    {
        public JCIFSScheme()
        {
            super(new JCIFSEngine());
        }
    }
    

    JCIFSEngine.java

    
    package ...;
    
    import java.io.IOException;
    
    import jcifs.ntlmssp.Type1Message;
    import jcifs.ntlmssp.Type2Message;
    import jcifs.ntlmssp.Type3Message;
    import jcifs.util.Base64;
    
    import org.apache.http.impl.auth.NTLMEngine;
    import org.apache.http.impl.auth.NTLMEngineException;
    
    public class JCIFSEngine implements NTLMEngine
    {
        public String generateType1Msg(String domain, String workstation) throws NTLMEngineException
        {
    
            Type1Message t1m = new Type1Message(Type1Message.getDefaultFlags(), domain, workstation);
            return Base64.encode(t1m.toByteArray());
        }
    
    
        public String generateType3Msg(String username, String password, String domain, String workstation, String challenge)
                throws NTLMEngineException
        {
            Type2Message t2m;
            try
            {
                t2m = new Type2Message(Base64.decode(challenge));
            } catch (IOException ex)
            {
                throw new NTLMEngineException("Invalid Type2 message", ex);
            }
            Type3Message t3m = new Type3Message(t2m, password, domain, username, workstation, 0);
            return Base64.encode(t3m.toByteArray());
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      非常感谢本,干得好。 对于我的解决方案,我需要根据您的课程进行 2 项改进。

      1) JcifsNtlmScheme 类

      jcifs中的接口发生了变化(我用的是1.3.14版本)。 NTLM 标志是必需的,我不太确定,但 0x82 对我有用。

      int flags = Type3Message.NTLMSSP_NEGOTIATE_OEM | Type3Message.NTLMSSP_NEGOTIATE_LM_KEY;
      
      Type3Message msg3 =
              new Type3Message(msg2, ntcredentials.getPassword(),
              ntcredentials.getDomain(), ntcredentials.getUserName(), ntcredentials.getHost(), flags);
      

      2) 类 NtlmJcifsCredentials

       DefaultHttpParams.setHttpParamsFactory(paramFact);
      

      这适用于第一次连接。这似乎是一个全局设置。它可能不是真正的线程安全的。 我需要基于连接的凭据。所以我删除了这个类并在创建 web 服务存根后直接插入了内置的 Authenticator:

      jcifs.Config.setProperty("jcifs.encoding", "ASCII");
      AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, JcifsNtlmScheme.class);
      
      Authenticator authenticator = new Authenticator();
      List<String> authScheme = new ArrayList<String>();
      authScheme.add(Authenticator.NTLM);
      authScheme.add(Authenticator.BASIC);
      authenticator.setAuthSchemes(authScheme);
      authenticator.setUsername(myusername);
      authenticator.setPassword(mypassword);
      authenticator.setHost(servername);
      authenticator.setDomain(domain);        
      exService._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);       
      exService._getServiceClient().getOptions().setProperty(HTTPConstants.CHUNKED, Boolean.FALSE);
      exService._getServiceClient().getOptions().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Boolean.TRUE);
      

      【讨论】:

        【解决方案5】:

        这个Axis2Patch.zip 是一个真正的救星。 这就是我所做的:

        使用内置 NTLMv2 的 httpclient4.1 beta1 编译 Axis2Patch。 将其导入我的项目并导入 httpclient4.1beta1 。

        我像这样更改了我的导入:

        import org.apache.commons.httpclient.auth.AuthenticationException;
        import org.apache.commons.httpclient.auth.NTLMScheme;
        //import org.apache.commons.httpclient.NTCredentials;
        //import org.apache.commons.httpclient.auth.AuthPolicy;
        
        import org.apache.http.auth.AuthScope;
        import org.apache.http.auth.NTCredentials;
        import org.apache.http.auth.UsernamePasswordCredentials;
        import org.apache.http.client.CredentialsProvider;
        import org.apache.http.client.params.AuthPolicy;
        

        无需太多代码更改,它就可以完美运行。 谢谢!

        【讨论】:

          猜你喜欢
          • 2011-09-08
          • 1970-01-01
          • 2021-11-16
          • 2011-07-30
          • 1970-01-01
          • 2014-04-07
          • 2019-05-18
          • 1970-01-01
          • 2020-06-04
          相关资源
          最近更新 更多