HttpInvoker SSL双向认证远程接口调用

在工作中,遇到需要开放一个web接口给对方调用,所以用到了HttpInvoker ,但是又遇到了一个问题,就是采用的是双向认证,只开放443端口,也就是说配置接口必须采用https,而且调用接口时,还需要通过双向认证。下面是我做的事例,贴出来供大家参考。

一、创建接口和接口实现类

public interface SyncRemoteService {
    /**
     * 挂网成功后需要更新数据列表
     *
     * @return List<List<String>>
     */
    public void syncBackDatas(List<List<String>> syncDatas);
    /**
     * 挂网成功后需要更新数据内容
     *
     * @param json 字符串
     */
    public void syncBackData(String json);
    
}

二、HttpInvoker 服务端配置

applicationContext.xml

<!-- 挂网返回结果接口 -->
    <bean id ="syncRemoteService" class = "com.noah.nrmp.resource.file.service.impl.SyncRemoteServiceImpl" />  
    
    <bean name ="syncRemoteServiceExporter" class = "org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" >
         <property name = "service" ref = "syncRemoteService" />
         <property name = "serviceInterface" value = "com.noah.nrmp.resource.file.service.SyncRemoteService" />
    </bean>


web.xml 文件配置

<servlet>
        <servlet-name>syncRemoteServiceExporter</servlet-name>
        <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>syncRemoteServiceExporter</servlet-name>
        <url-pattern>/SyncRemoteService</url-pattern>
    </servlet-mapping>  

二、HttpInvoker 客户端配置

需要commons-httpclient-3.1.jar包,还需要什么依赖包,自己查下资料吧

applicationContext.xml

 <bean id="syncRemoteService"
        class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
        <property name="serviceUrl"
            value="https://www.nrmp2.anoah/NRMP_TEST/SyncRemoteService" />
        <property name="serviceInterface" value="com.noah.nrmp.resource.file.service.SyncRemoteService" />
        <property name="httpInvokerRequestExecutor">
               <bean class="com.noah.nrmp.resource.file.service.SSLCommonsHttpInvokerRequestExecutor"/>
        </property>
   </bean>

因为这里需要双向认证,所以我自己实现了CommonsHttpInvokerRequestExecutor,方便加入SSL证书

SSLCommonsHttpInvokerRequestExecutor 内容如下:

public class SSLCommonsHttpInvokerRequestExecutor extends CommonsHttpInvokerRequestExecutor {
    private HttpClient httpClient;
    
    @SuppressWarnings("deprecation")
    public SSLCommonsHttpInvokerRequestExecutor(){
        Protocol.registerProtocol("https", new Protocol("https", new SimpleSSLTestProtocolSocketFactory(),443));  
        this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
    }
    
    public SSLCommonsHttpInvokerRequestExecutor(HttpClient httpClient) {
        this.httpClient = httpClient;
    }
    
    @Override
    public void setHttpClient(HttpClient httpClient) {
        this.httpClient = httpClient;
    }
    @Override
    public HttpClient getHttpClient() {
        return httpClient;
    }
    
}

关键是加入这句话:Protocol.registerProtocol("https", new Protocol("https", new SimpleSSLTestProtocolSocketFactory(),443));  

在SimpleSSLTestProtocolSocketFactory中加载客户端证书和服务端证书

SimpleSSLTestProtocolSocketFactory 内容如下:

public class SimpleSSLTestProtocolSocketFactory implements SecureProtocolSocketFactory {
    private static final Log LOG = LogFactory.getLog(SimpleSSLTestProtocolSocketFactory.class);
    private static SSLContext SSLCONTEXT = null;
    private static SSLContext createSSLContext() {
        try {
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            FileInputStream ksInstream = new FileInputStream(new File("D:\\client.p12"));
            
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream trustInstream = new FileInputStream(new File("D:\\jssecacerts"));
            try {
                keystore.load(ksInstream, "test".toCharArray());
                trustStore.load(trustInstream, "change".toCharArray());
            } finally {
                if (ksInstream != null)
                    ksInstream.close();
                if (trustInstream!=null)
                    trustInstream.close();
            }
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keystore, "test".toCharArray());
            trustManagerFactory.init(trustStore);
            KeyManager[] keymanagers=keyManagerFactory.getKeyManagers();
            TrustManager[] trustmanagers = trustManagerFactory.getTrustManagers();
            SSLContext sslcontext = SSLContext.getInstance("TLS");
            sslcontext.init(keymanagers, trustmanagers, null);
            return sslcontext;
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
            throw new IllegalStateException(ex.getMessage());
        }

    }

    private static SSLContext getSSLContext() {
        if (SSLCONTEXT == null) {
            SSLCONTEXT = createSSLContext();
        }
        return SSLCONTEXT;
    }

    public SimpleSSLTestProtocolSocketFactory() {
        super();
    }

    public Socket createSocket(final String host, final int port,
            final InetAddress localAddress, final int localPort,
            final HttpConnectionParams params) throws IOException,
            UnknownHostException, ConnectTimeoutException {
        if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }
        int timeout = params.getConnectionTimeout();
        if (timeout == 0) {
            return createSocket(host, port, localAddress, localPort);
        } else {
            // To be eventually deprecated when migrated to Java 1.4 or above
            return ControllerThreadSocketFactory.createSocket(this, host, port,
                    localAddress, localPort, timeout);
        }
    }

    public Socket createSocket(String host, int port, InetAddress clientHost,
            int clientPort) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port,
                clientHost, clientPort);
    }

    public Socket createSocket(String host, int port) throws IOException,
            UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port);
    }

    public Socket createSocket(Socket socket, String host, int port,
            boolean autoClose) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(socket, host,
                port, autoClose);
    }
}

其中 keystore.load(ksInstream, "test".toCharArray());
         trustStore.load(trustInstream, "changeit".toCharArray()); 

test 和changeit为证书密码


FileInputStream trustInstream = new FileInputStream(new File("D:\\jssecacerts"));


jssecacerts 文件生成代码 如下:

public class InstallCert {

    public static void main(String[] args) throws Exception {
        String host;
        int port;
        char[] passphrase;
        if ((args.length == 1) || (args.length == 2)) {
            String[] c = args[0].split(":");
            host = c[0];
            port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            String p = (args.length == 1) ? "changeit" : args[1];
            passphrase = p.toCharArray();
        } else {
            System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
            return;
        }

        File file = new File("jssecacerts");
        if (file.isFile() == false) {
            char SEP = File.separatorChar;
            File dir = new File(System.getProperty("java.home") + SEP
                    + "lib" + SEP + "security");
            file = new File(dir, "jssecacerts");
            if (file.isFile() == false) {
                file = new File(dir, "cacerts");
            }
        }
        System.out.println("Loading KeyStore " + file + "...");
        InputStream in = new FileInputStream(file);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();

        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory factory = context.getSocketFactory();

        System.out.println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }

        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }

        BufferedReader reader =
                new BufferedReader(new InputStreamReader(System.in));

        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = chain[i];
            System.out.println
                    (" " + (i + 1) + " Subject " + cert.getSubjectDN());
            System.out.println("   Issuer  " + cert.getIssuerDN());
            sha1.update(cert.getEncoded());
            System.out.println("   sha1    " + toHexString(sha1.digest()));
            md5.update(cert.getEncoded());
            System.out.println("   md5     " + toHexString(md5.digest()));
            System.out.println();
        }

        System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
        String line = reader.readLine().trim();
        int k;
        try {
            k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
        } catch (NumberFormatException e) {
            System.out.println("KeyStore not changed");
            return;
        }

        X509Certificate cert = chain[k];
        String alias = host + "-" + (k + 1);
        ks.setCertificateEntry(alias, cert);

        OutputStream out = new FileOutputStream("jssecacerts");
        ks.store(out, passphrase);
        out.close();

        System.out.println();
        System.out.println(cert);
        System.out.println();
        System.out.println
                ("Added certificate to keystore 'jssecacerts' using alias '"
                        + alias + "'");
    }

    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();

    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }

    private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }

}


好了,大致就这样,就能实现HttpInvoker 双向认证 接口调用了

猜你喜欢

转载自blog.csdn.net/hongfu951/article/details/19160989
今日推荐