【问题标题】:Apache CXF soap client using jCIFS SSL + NTLM authentication JDK 5使用 jCIFS SSL + NTLM 身份验证 JDK 5 的 Apache CXF 肥皂客户端
【发布时间】:2014-10-15 02:03:34
【问题描述】:

我正在尝试使用Apache CXF 中提到的 NTLM 身份验证对 SOAP Web 服务进行身份验证,堆栈如下 -

  • jcifs-1.3.17.jar
  • cxf-2.7.11
  • NTLM + SSL
  • JDK 5(我可能无法更改)

每次我尝试连接时,它都会拒绝 401 未经授权的访问,因为它使用了我未经授权的底层 NT 凭据,而不是我在代码中配置的有效凭据。 (我不得不修改 jCIFS,因为它不支持 SSL + NTLM 以返回 NtlmHttpURLConnection 的 HTTPs 版本)。使用 HTTP 异步机制时的类似结果。

String domainController = "xxx.xxx.xxx";
UniAddress dc = UniAddress.getByName(domainController, true);

jcifs.Config.setProperty("http.auth.ntlm.domain", "xxx.xxx.xxx");
jcifs.Config.setProperty("jcifs.smb.client.domain", "domain");
jcifs.Config.setProperty("jcifs.netbios.wins", dc.getHostAddress());
jcifs.Config.setProperty("jcifs.smb.client.soTimeout", "300000"); // 5 minutes
jcifs.Config.setProperty("jcifs.netbios.cachePolicy", "1200"); // 20 minutes
jcifs.Config.setProperty("jcifs.smb.client.username", USER);
jcifs.Config.setProperty("jcifs.smb.client.password", PWD);

//Register the jcifs URL handler to enable NTLM
jcifs.Config.registerSmbURLHandler();

    HelloWorld src = new HelloWorld();

    ClientProxyFactoryBean factory = new ClientProxyFactoryBean(new JaxWsClientFactoryBean());  

    factory.setServiceClass( IHelloWorld.class );  
    factory.setAddress(SERVICE_URL);
    factory.setUsername(USER);  
    factory.setPassword(PWD);
    IHelloWorld service = (IHelloWorld ) factory.create();

    Client client = ClientProxy.getClient(service);
    HTTPConduit http = (HTTPConduit) client.getConduit();
    System.out.println(http.getClass().getName());
    //org.apache.cxf.transport.http.URLConnectionHTTPConduit

    HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
    httpClientPolicy.setConnectionTimeout(36000);
    httpClientPolicy.setAllowChunking(false);

    http.setClient(httpClientPolicy);
    http.getAuthorization().setAuthorizationType("NTLM");
    http.getAuthorization().setUserName(USER);
    http.getAuthorization().setPassword(PWD);

    http.getClient().setAllowChunking( false );
    http.getClient().setAutoRedirect( true );

    TLSClientParameters tcp = new TLSClientParameters();  
    tcp.setTrustManagers( new TrustManager[]{ new TrustAllX509TrustManager() } );  
    http.setTlsClientParameters( tcp );

    System.out.println("Invoking service...");
    String msg= "echo";
    try {
        String res = service.readMessage(msg);
        System.out.println("readMessage.result=" + res);

    } catch (Exception e) { 
        e.printStackTrace();
    }

运行此代码后,我得到以下异常跟踪

: domain\ 是未经授权的用户 sun.reflect.NativeConstructorAccessorImpl.newInstance0(本机方法) 在 sun.reflect.NativeConstructorAccessorImpl.newInstance(未知 来源)在 sun.reflect.DelegatingConstructorAccessorImpl.newInstance(未知 来源)在 java.lang.reflect.Constructor.newInstance(未知来源) 在 org.apache.cxf.interceptor.ClientFaultConverter.processFaultDetail(ClientFaultConverter.java:175) 在 org.apache.cxf.interceptor.ClientFaultConverter.handleMessage(ClientFaultConverter.java:78) 在 org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272) 在 org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113) 在 org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69) 在 org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34) 在 org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272) 在 org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:845) 在 org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1624) 在 org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1513) 在 org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1318) 在 org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56) 在 org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:632) 在 org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62) 在 org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272) 在 org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:570) 在 org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:479) 在 org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:382) 在 org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:335) 在 org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96) 在 org.apache.cxf.frontend.ClientProxy.invoke(ClientProxy.java:81) 在 com.sun.proxy.$Proxy44.readMessage(Unknown Source)

【问题讨论】:

    标签: ssl cxf soap-client ntlm jcifs


    【解决方案1】:

    CXF 2.7.x 不支持 JDK 5。来自CXF FAQ

    CXF 可以与 JDK 1.5 一起运行吗?

    是的,适用于 CXF 2.6.x 及更早版本。请记住,Java 2 SE 5.0 JDK 1.5 的生命周期结束 (EOL)。 CXF 2.7.x 不再 支持 Java 5。要升级到 2.7.x,您必须使用 Java 6(或更新)。

    【讨论】:

    • 不幸的是,对于应用程序的其余部分,我们被困在 JDK 1.5 堆栈中,因此我们决定使用 HttpClient4.x 编写自定义实现
    • @ManishDevraj 只是好奇:并不是说推出自己的解决方案有什么问题,而是 CXF 2.6.x 不是一个选项吗?
    • 我认为根据我的诊断,由于某种原因根本没有使用 jCIFS,在 CXF 或 Axis2 版本之间切换不是问题......
    【解决方案2】:

    在 JDK 5 堆栈和其他 SOAP 框架(如 Axis2 和 CXF)之间挣扎了数小时后,我终于设法使用了一个原始 SOAP 客户端,它几乎可以完成我需要的工作。以下是帮助完成它的代码,即使使用自定义 NT 登录而不是底层登录。

    public final class JCIFSEngine implements NTLMEngine {
    
        private static final int TYPE_1_FLAGS = NtlmFlags.NTLMSSP_NEGOTIATE_56
                | NtlmFlags.NTLMSSP_NEGOTIATE_128
                | NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2
                | NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
                | NtlmFlags.NTLMSSP_REQUEST_TARGET;
    
        public String generateType1Msg(final String domain, final String workstation)
                throws NTLMEngineException {
            final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS,
                    domain, workstation);
            return Base64.encode(type1Message.toByteArray());
        }
    
        public String generateType3Msg(final String username,
                final String password, final String domain,
                final String workstation, final String challenge)
                throws NTLMEngineException {
            Type2Message type2Message;
            try {
                type2Message = new Type2Message(Base64.decode(challenge));
            } catch (final IOException exception) {
                throw new NTLMEngineException("Invalid NTLM type 2 message",
                        exception);
            }
            final int type2Flags = type2Message.getFlags();
            final int type3Flags = type2Flags
                    & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
            final Type3Message type3Message = new Type3Message(type2Message,
                    password, domain, username, workstation, type3Flags);
            return Base64.encode(type3Message.toByteArray());
        }
    
    }
    
    
    public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {
    
        public AuthScheme create(final HttpContext context) {
            return new NTLMScheme(new JCIFSEngine());
        }
    }
    

    使用 HttpClient 对象注册自定义 NTLM Engine 和 Auth 方案注册表 -

    protected Registry<AuthSchemeProvider> getAuthRegistry() {
            Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder
                    .<AuthSchemeProvider> create()
                    .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
                    .build();
            return authSchemeRegistry;
        }
    
    protected CredentialsProvider getCredentialsProvider(String user,
                String pass, String domain) {
            CredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST,
                    AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.NTLM),
                    new NTCredentials(user, pass, null, domain));
            return credsProvider;
        }
    
            HttpClientBuilder httpClientBuilder = HttpClients.custom();
            httpClientBuilder.setDefaultAuthSchemeRegistry(getAuthRegistry());
            httpClientBuilder
                    .setDefaultCredentialsProvider(getCredentialsProvider(
                            config.getUserName(), config.getPassword(),
                            config.getDomain()));
    
            if (config.isProxy()) {
                HttpHost proxy = new HttpHost(config.getProxyHost(),
                        config.getPort());
                httpClientBuilder.setProxy(proxy);
            }
    
            httpClientBuilder.build();
    

    希望这对有类似问题的人有所帮助。干杯

    【讨论】:

      猜你喜欢
      • 2016-11-03
      • 2014-08-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 1970-01-01
      相关资源
      最近更新 更多