本文共 5097 字,大约阅读时间需要 16 分钟。
关于weblogic host name varify 的一个问题
问题是项目组一个同事发现的。 出问题的代码如下: 1 2 3 4 5 6 7 8 9 10 | StringBuilder document = new StringBuilder(); URL url = new URL(Constants.URL); // 远程url,实际值为:https://mapi.alipay.com/... URLConnection conn = url.openConnection(); BufferedReader reader = new BufferedReader( new InputStreamReader( conn.getInputStream())); String line = null ; while ((line = reader.readLine()) != null ) { document.append(line + " " ); } reader.close(); |
当在weblogic服务器上运行时,抛出了如下异常:
1 2 3 4 5 6 7 8 9 | < 2013 - 10 - 22 下午 02 时 11 分 12 秒 CST> <Error> <HTTP> <BEA- 101019 > <[ServletContext @25331129 [app:httpTest module:WebContent path:/TestHTTPS spec-version: 2.5 ]] Servlet failed with IOException javax.net.ssl.SSLKeyException: [Security: 090504 ]Certificate chain received from mapi.alipay.com - 110.75 . 142.31 failed hostname verification check. Certificate contained *.alipay.com but check expected mapi.alipay.com at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireException(Unknown Source) at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireAlertSent(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source) at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source) at com.certicom.tls.record.handshake.ClientStateReceivedServerHello.handle(Unknown Source) Truncated. see log file for complete stacktrace > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | StringBuffer result = new StringBuffer(); HttpClient httpClient = new DefaultHttpClient(); // 使用的请求方式是GET HttpGet httpGet = new HttpGet(Constants.URL); try { HttpResponse response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); if (httpEntity != null ) { InputStream inputStream = httpEntity.getContent(); byte [] responseByte = new byte [ 2048 ]; int len = 0 ; while ((len = inputStream.read(responseByte)) != - 1 ) { result.append( new String(responseByte, 0 , len)); } } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭http请求 httpClient.getConnectionManager().shutdown(); } |
最主要的修改点,就是将直接用URL打开URLConnection的方法用HttpClient进行了改写。改写之后,即使weblogic服务器的配置仍然是“BEA 主机名验证器”,https请求也能够畅通无阻。
之所以能够成功,是因为HttpClient中,对发起的Https请求会附加一个主机名验证器,默认的是BrowserCompatHostnameVerifier。这个验证器与Firefox的工作方式是相同的,它可以匹配所有子域的通配符(比如”*.foo.com”)。HttpClient还有两个主机名验证器,按下不表。 然后,那位同事和我们讨论了一下为什么使用了HttpClient之后,weblogic上配置的主机名验证器就失效了呢? weblogic在对外发出https请求时,会将这个请求封装为一个“weblogic.security.SSL.SSLClientInfo”类的实例。在这个实例中,有一个字段是“private HostnameVerifier hostnameVerifier;”。weblogic在通过“weblogic.security.utils.SSLSetup"类来构建这个实例的时候,会判断原有连接中是否自带有HostnameVerifier。如果有,则使用原配;否则,就会按照weblogic服务器的配置,组装上一个主机名验证器。 所以,当使用了HttpClient之后,服务器就会按照HttpClient自带的主机名验证器进行验证,而自己的配置却被置之脑后了。 除了使用HttpClient,还有两种方式可以解决这个问题。 其一是仿照HttpClient的思路,为Https请求带上一个自定义的主机名验证器, 代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | URL url = new URL(Constants.URL); // 远程url HttpsURLConnection conn = new HttpsURLConnection(url); conn.setHostnameVerifier( new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { System.out.println( "这里是自定义的特殊处理:只打印,不校验" ); return true ; } }); System.out.println(conn.getHostnameVerifier()); BufferedReader reader = new BufferedReader( new InputStreamReader( conn.getInputStream())); String line = null ; while ((line = reader.readLine()) != null ) { document.append(line + " " ); } reader.close(); |
这里使用的是HttpsURLConnection,因为只有这个类,才有setHostnameVerifier等相关接口。
另一种方式是为weblogic自定义一个主机名校验器,并为服务器做“定制主机名验证器”的相关配置。 自定义校验器的代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package verifier; import javax.net.ssl.SSLSession; import weblogic.security.SSL.HostnameVerifier; /** * 自定义的主机名验证类 * @since 2013-10-14 * @version 1.0.0 */ public class CustomerVerifier implements HostnameVerifier { /** * 验证方法。 * @since 2013-10-14 * @param arg0 * @param arg1 * @return * @see weblogic.security.SSL.HostnameVerifier#verify(java.lang.String, * javax.net.ssl.SSLSession) * */ @Override public boolean verify(String arg0, SSLSession arg1) { System.out.println( "this is the CustomerVerifier by Loy, arg0 = " + arg0 + ", arg1 = " + arg1); return true ; } } |
在服务器上做如下配置:
1、主机名验证选项选择“定制主机名验证器”; 2、“定制主机名验证器:”输入框中输入自定义的类的全路径类名,如“verifier.CustomerVerifier”; 3、将自定义类的class文件放到weblogic服务器的ClassPath下,如将其导出为jar包,并在weblogic启动脚本中加入配置:set CLASSPATH=%CLASSPATH%;D:\bea\user_projects\domains\car_domain_10\lib\CustomerVerifier.jar 配置完成后,重启服务器,然后再次运行最早的那段会抛出异常的代码。正常情况下,这个请求就能够获得返回数据了。本文转自 斯然在天边 51CTO博客,原文链接:http://blog.51cto.com/winters1224/1313111,如需转载请自行联系原作者