根据XX公网前置接入规范,XX后台支持公网接入。但必须走https协议加密传输。
java很强大,在java上调用http接口以及解析json之类的应该很容易,有很多现成的接口和第三方库如httpclinet,okhttp,volley,retrofit等。
但是,XX的这套接口涉及到https加密传输,需加载本地公钥证书,以及定制http协议报文头,并且post的是16进制数据。
网上查资料,大多都是讲如何post提交json的,以及如何上传二进制图片的等内容。没找到如何提交二进制流的。
于是自己摸索了吧,接口通了。在此总结下,非专业andorid出身,代码有点乱。但要点就那几个。
使用的是retrofit.
要实现的协议格式如下:
POST/mjc/webtrans/VPB_lb HTTP/1.1
HOST:xxxxxxxx:xxxxx
User-Agent: Donjin Http0.1
Cache-Control: no-cache
Content-Type:x-XX-XXXX/x-XX
Accept: */*
Content-Length: xx
00 XX XX 00 0X 00 00 XX XX 00 XX XX XX XX00 00 20 00 00 00 c0 00 16 00 00 01 31 30 30 30 30
要点1,自定义http报文头,这个很简单了,举例如下:
@Headers({"User-Agent: Donjin Http 0.1","Cache-Control: no-cache","Content-Type: x-xxx","Accept-Encoding: *","Content-Length: xx"})//需要添加头
// @Multipart
@POST("/")
Call<LoginResult> postData(@Body RequestBody bodyhex);
需要注意的是,content-lenth这个别按上面的写固定了,这个是根据要提交的16进制数据来的。他不是一个固定值。
提交的数据内容直接传参 @Body RequestBody bodyhex
传的参数怎么处理,让他能够以16进制post呢?
如下,重新overrite RequestBody中的writeto方法和contenlenth放法,注意,必须得有contenlenth方法,否则会出错,不知道你post的数据有多少。
示例如下:
class DataBean {
public byte[] data;
public String DA = "00000000000000c0006300011000000010030002953657175656e6365204343131390003303120";//要发送的16进制数据的字符串形式
public RequestBody bodyhex;
public DataBean()
{
data = hexStringToBytes(DA);//转为hex 16进制
bodyhex = new RequestBody() {
@Override
public MediaType contentType() {
return null;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.write(data);
}
@Override
public long contentLength() {
return data.length;
}
};
};
}
还有一个比较重要的地方就是加载https的本地公钥证书啦
/** * 通过okhttpClient来设置证书 * @param clientBuilder OKhttpClient.builder * @param certificates 读取证书的InputStream */ public static void setCertificates(OkHttpClient.Builder clientBuilder, InputStream... certificates) { try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory .generateCertificate(certificate)); try { if (certificate != null) certificate.close(); } catch (IOException e) { } } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); clientBuilder.sslSocketFactory(sslSocketFactory,trustManager); } catch (Exception e) { e.printStackTrace(); } }
public static OkHttpClient getOkHttpSingletonInstance( Context ct) { mContext = ct; if (okHttpClient == null) { synchronized (OkHttpClient.class) { if (okHttpClient == null) { okHttpClient = new OkHttpClient(); //设置合理的超时 OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder() .readTimeout(3, TimeUnit.SECONDS) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) //设置连接超时 30秒 .writeTimeout(3, TimeUnit.MINUTES) //.addInterceptor(new LoggingInterceptor())//添加请求拦截 .addNetworkInterceptor( new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS)) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }) //.sslSocketFactory(sslParams.sSLSocketFactory,sslParams.trustManager) .retryOnConnectionFailure(true); //InputStream in = new InputStream() try { //AssetManager assetManager = AssetManager.class.newInstance(); InputStream is = MyAppcation.getInstance().getAssets().open("UP.pem"); OkHttp3Utils.setCertificates( httpBuilder,is); } catch (IOException e) { e.printStackTrace(); } okHttpClient = httpBuilder.build(); } } } return okHttpClient; }完成以上这些,就够了。能够实现一开始的要求了。最后完善下接口,在自定义一个
ConverterFactory
对应答的报文解析就完整了。
测试结果如下:
06-15 03:37:45.183 4061-4099/com.example.yang.myapplication D/OkHttp: Connection: Keep-Alive
--> END POST
06-15 03:37:45.585 4061-4099/com.example.yang.myapplication D/OkHttp: <-- 200 OK https://xx.xx.1xx.xx:xxx0/ (400ms)
06-15 03:37:45.589 4061-4099/com.example.yang.myapplication D/OkHttp: Allow: POST, PUT
06-15 03:37:45.590 4061-4099/com.example.yang.myapplication D/OkHttp: Content-Type: x-ISO-xxx/x-xx
06-15 03:37:45.591 4061-4099/com.example.yang.myapplication D/OkHttp: Date: Fri, 15 Jun 2018 03:39:44 GMT
06-15 03:37:45.592 4061-4099/com.example.yang.myapplication D/OkHttp: Content-Length: 112
Server: Access-Guard-1000-Software/1.0
06-15 03:37:45.603 4061-4099/com.example.yang.myapplication D/OkHttp: Connection: close
06-15 03:37:45.604 4061-4099/com.example.yang.myapplication D/OkHttp: <-- END HTTP
06-15 03:37:45.633 4061-4099/com.example.yang.myapplication
D/respond:: 00XXXXXXXXXXXXXXXXXXXXXXXXXXX600000001003000296f3136333135305358582d3443333034313139
06-15 03:37:45.704 4061-4061/com.example.yang.myapplication D/AA: 成功Response{protocol=http/1.1, code=200, message=OK, url=https://XXXXXXXX;XXX/}
D/OkHttp: --> POST https://1xx.xx.xx.xx:xxxx/ http/1.1
Content-Type: x-ISO-TPDU/x-auth
D/OkHttp: Content-Length: 89
User-Agent: Donjin Http 0.1
Cache-Control: no-cache
Accept-Encoding: *
D/OkHttp: Host: xxx.xxx.xxx.xx:xxxx
Connection: Keep-Alive
--> END POST
D/OkHttp: <-- 200 OK https://xxx.xxx.xxx.xx:xxxx/ (421ms)
D/OkHttp: Allow: POST, PUT
Content-Type: x-ISO-TPDU/x-auth
Date: Sat, 30 Jun 2018 09:58:41 GMT
D/OkHttp: Content-Length: 123
D/OkHttp: Server: Access-Guard-1000-Software/1.0
Connection: close
<-- END HTTP
D/AA: 成功
Response{protocol=http/1.1, code=200, message=OK, url=https://xxxxxxxx:xxxxx/}
D/respondAA:: 007960000005016131003111080810003800010ac0001400000117563506300800094900313735363335353837303233303037333738323231343839383431313334313331303031340011000007500030004050bc9eb4774a92544c29dad2c764150bb93eba92d9f11a222efa9c2300000000000000002b580802
I/System.out: 开始解析...
I/System.out: ->ok 解析成功!
I/System.out: pinkey:b1a7ab3cb49c9757390f39a19ce71ae7
I/System.out: <-Er PIK错误