一个解决问题的思路 Didn’t find class “javax.net.ssl.SNIHostName”

TL;DR

Android开发,targetSdkVersion 23,任务是给APP集成AWS Android IoT SDK(2.16.12),Android Studio build APP过程中遇到了标题所示的问题。

原因是SDK其中的一个依赖'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.2' 有个bug

我的解决方式是:Downgrade AWS SDK Android IoT SDK 到2.14.2,因为这个版本依赖的是org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0,该版本没有兼容旧版本API Level 23的问题。

4天前( 2020Jun19) AWS SDK Android已经发布2.16.13 fix了上述的问题,Issue是: https://github.com/aws-amplify/aws-sdk-android/pull/1572

正文

这篇文章我想表达的点是,可以通过降低版本去解决新版可能导致的兼容性问题。

但是在我遇到标题所示的问题时,我并没有马上降低AWS SDK的版本,我的解决过程是:

1. Google Search Android Studio console the build error:

关键信息是: Didn’t find class “javax.net.ssl.SNIHostName”

2. 定位到这个Github Issue

https://github.com/eclipse/paho.mqtt.java/issues/633

3. 看了上述很长的Issue之后,发现是 org.eclipse.paho:org.eclipse.paho.client.mqttv3 的版本1.2.2的有Bug

但是1.2.3已经解决,而且release了。

https://github.com/eclipse/paho.mqtt.java/milestone/9?closed=1

4. 发现AWS SDK Android 2.16.12 Gradle 依赖的正是 1.2.2版本😂

SDK 2.16.12 的发布时间是11 Apr。其实只要AWS SDK更新一下 mqtt的版本到1.2.3即可解决问题。

https://github.com/aws-amplify/aws-sdk-android/blob/release_v2.16.12/aws-android-sdk-iot/build.gradle

5. 我当时想到的解决问题的方式

5.1 给AWS SDK 提交PR去更新mqtt的版本到1.2.3,然后等待review release。

5.2 自己把AWS SDK 源码下来了,然后本地修改,本地导入修改过的module。

我用的是5.2的方式,aws-android-sdk-iot只是整个sdk的一个gradle subproject。

这个类库的 gradle 依赖链是: aws-android-sdk-iot => aws-android-sdk-core & aws-android-sdk-testutils

也就是说,为了能修改aws-android-sdk-iot的gradle依赖,我要把相关类库的源码都放在自己的代码库里面,这个方式有点 overkill了。最后我还是放弃了这个方法。

我是如何想到Downgrade的?

问题没有解决,我就下班了。下班后一直还在想着怎么fix,洗澡过程中突然灵感乍现,为何不downgrade呢?

于是开电脑,检查AWS SDK android IoT Git file changes history找到了一个合适的版本后,修改build.gradle,sync gradle之后 build app 马上成功。👍

API服务监听 0.0.0.0 & 如何给本地localhost API使用HTTPS (API Gateway, SSH远程端口转发)

TL;DR

API 服务监听 0.0.0.0 IP地址,既可以被同一个局域网的主机通过改API主机的IP访问。

正文

后端开发中,各种API服务(比如,Django, Flask, Express.js)启动的时候,一般默认监听的都是127.0.0.1地址。

如果要使用DHCP分配的IP地址访问,可以把host改为0.0.0.0再启动API服务。这样同局域网的主机,比如说前端的同事就可以通过你的IP地址访问后端API了。

0.0.0.0的意思是分配给改主机的所有可能的IPv4地址。IPv6是::/0

可能分配给主机的IP地址是未知的,但是你启动API的时候是需要绑定一个IP地址的,如果你没有分配到IP地址,这个时候127.0.0.1就有用了,或者localhost也可以。

如果启动API的时候绑定不是你主机的IP会怎样?

你应该会有绑定失败,Django会抛出这个错误: Error: That IP address can't be assigned to.

所以,0.0.0.0就是有点类似通配符的效果。不需要指定Hard code 未知的地址。

竟然0.0.0.0这么方便,那些API框架怎么默认不使用?

我觉得是为了安全,0.0.0.0会直接让你服务器可能直接暴露在公网里面。一般开发过程中都是没有什么认证措施的,这个时候附近有个人端口扫描一下,可能就会泄露重要的资料。


为什么有了这个文章?

近期空闲时间在写Side project,需要暴露自己的API服务在公网,给一个SaaS服务回调。

当然我可以用ngrok,但是对比于4年前,这个服务现在越来越抠门。以前是可以单机多开,而且不限时长,现在玩了付费订阅制之后,限制越来越多。

我的解决方案是:

  1. API服务监听0.0.0.0端口,给路由器访问。
  2. 路由器做端口转发,让宽带公网IP的某个端口映射到本机API服务端口。
    1. 为什么不使用路由器DMZ?
    2. DMZ会直接暴露本机到公网。只使用端口转发所需的服务,这样安全点。

最后我把这个地址放到SaaS,即可访问http://58.123.123.123:8000/

延伸一下,如何给SaaS使用上HTTPS,而不是直接用本机公网地址?

方案一 AWS API Gateway Proxy :

SaaS Callback URL => AWS API Gateway => Public IP => Router Port forwarding => API (Listen 0.0.0.0)

  1. AWS API Gateway会给你分配一个HTTPS地址,你需要创建一个Stage才会有。
  2. 直接创建一个Resource,然后把所有路由都转发到本机的公网IP,比如上述的http://58.123.123.123:8000/
  3. 如果你不想使用AWS API Gateway分配的域名,你可以使用AWS ACM认证域名,然后在API Gateway创建一个漂亮的custom domain。
  4. 重要的是,上述的1 2 3都是免费的。如果超出了AWS free tier期限,AWS API Gateway是按需付费,零星的使用量基本上是免费的。
  5. ⚠️这个不是全程加密,API Gateway到本机的API服务之间的通信没有使用HTTPS。

方案二 本站Apache反向代理 + SSH远程转发(这个是我写这篇文章的时候想到的方法😂):

SaaS Callback URL => This WordPress Site Apache Reverse Proxy => SSH Remote forwarding => API (Listen on localhost)

  1. ✅ 这个是全程加密通信。
    1. SaaS到Apache是HTTPS,我还启用了Http2.0
    2. Apache到我本机的API服务是SSH。
  2. Apache的反向代理可以使用二级域名,然后创建一个VirtualHost。
  3. SSH使用的是远程转发。因为要远程主机访问本机服务,本机没有公网IP。

Apache VirtualHost代码:

<VirtualHost _default_:443>
  # 一个新的二级域名,记得配置DNS,还有SSL/TLS证书
  ServerName xx.adamliu.net
  SSLEngine on
  SSLCertificateFile "/opt/bitnami/apache2/conf/adamliu.net.crt"
  SSLCertificateKeyFile "/opt/bitnami/apache2/conf/adamliu.net.key"
  Protocols h2 h2c http/1.1

  ProxyPass "/"  "http://localhost:8000/"
  # 这句是让3xx之类的重定向地址可以被Apache捕获然后修改为apache的对外URL
  ProxyPassReverse "/"  "http://localhost:8000/"

  # Error Documents
  ErrorDocument 503 /503.html

</VirtualHost>

SSH远程端口转发:

# ServerAliveInterval和ServerAliveCountMax是为了保持session常驻
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5  -R localhost:8000:localhost:8000 ubuntu@18.123.123.123

[教表弟搭博客]📝课程记录 20200503 启用HTTPS&添加网站统计功能

视频工具: 腾讯会议

时长:约2小时

课程内容:

1 . 介绍一些计算机常用英文单词 enable, valid, renew, space-separated

2 . 介绍ChromeDev Tool的使用

3 . 动手把网址升级为HTTPS

4. 动手给网站添加统计分析功能(百度网站统计)

遇到的问题:

1 . HTTPS

使用 Bitnami 安装 WordPress 可以直接使用内置的命令 ./bncert-tool 根据命令指引完成HTTPS 证书的配置。

然而,我们遇到了这个问题, acme 400 Timeout during connect (likely firewall problem):

阿里云的 VM,包括 AWS 的 EC2 都是默认可以访问公网的。没有防火墙拦住。

这个问题卡了我们挺久的,时间问题,这次课程我们决定跳过HTTPS的配置。

最后我发现,原来没有给阿里云的 VM 开启443 访问端口。。。。尴尬呀😅。这个脚本 bncert-tool 会配置Apache的服务器开启443, 让 Let’s Encrypt 能访问你的443端口完成 Challenge。

2 . 添加百度网站统计功能

这个没遇到什么问题,验证完成的方式也很简单,直接在 Chrome Dev Tool 看源码就好了。

修改的是 WordPress 主题 header.php 文件

完。

[教表弟搭博客] 📝课程记录 20200414 去除Bitnami Logo 介绍Widget Menu

视频工具: 腾讯会议

时长:约2小时

课程内容:

1. 去除右下角Bitnami Logo

2. 添加菜单,新页面和小插件widgets

3. 使用Sendgrid配置WordPress邮件服务器 (本来想注册Sendgrid,但是注册后需要联系客服验证账户。最后改用阿里云邮件推送。使用阿里云服务不是课程原本的安排,我也没有配置过,是上课的过程临时想到的。配置邮件推送服务的过程需要验证域名,我让表弟新建了3条域名记录分别是TXT, MX和CNAME,让一个14岁的初中生玩这些,感觉很不错。)

遇到的问题:

1 . 阿里云邮件推送服务虽然成功配置,获取到了SMTP的账户信息,但是WordPress的WP Mail SMTP插件的配置过程遇到了问题。导致一直发不来信息。在检查这个插件Mail Test功能下面的隐藏很深的Debug Log之后,原来回信地址要和阿里云发信地址一致(也可以不一样,但是要在阿里云配置和验证回信地址)。把插件配置的From Email改为发信地址,问题解决。

需要强调的是,WP Mail SMTP插件的Mail Test功能,就算配置好了也不一定能成功发邮件,因为使用SMTP协议发这种测试邮件会很容易被认为是Spam。所以,请用Reset Passwrod功能测试你的WP Mail SMTP配置,这样更准确。

不知道为什么,就算Enable了SMTP SSL,阿里云也没有加密我的邮件。多亏了Google Magic,不然这种邮件一般都会去垃圾桶的。

对比于使用这种SMTP协议,使用Sendgrid更靠谱呀。