🛡️ HTTPS
为保证网络访问安全,现在大多数企业都会选择使用SSL验证来提高网站的安全性。
所以Forest自然也加入了对HTTPS的处理,现在支持单向认证和双向认证的HTTPS请求。
# 单向认证
如果访问的目标站点的SSL证书由信任的Root CA发布的,那么您无需做任何事情便可以自动信任
public interface Gitee {
    @Request(url = "https://gitee.com")
    String index();
}
 2
3
4
5
Forest的单向验证的默认协议为TLS,如果一些站点的API不支持该协议,您可以在全局配置中将ssl-protocol属性修改为其它协议,如:SSL, TLS。
forest:
  ...
  ssl-protocol: TLS
 2
3
全局配置可以配置一个全局统一的SSL协议,但现实情况是有很多不同服务(尤其是第三方)的API会使用不同的SSL协议,这种情况需要针对不同的接口设置不同的SSL协议。
/**
 * 在某个请求接口上通过 sslProtocol 属性设置单向SSL协议
 */
@Get(
    url = "https://localhost:5555/hello/user",
    sslProtocol = "TLS"
)
ForestResponse<String> truestSSLGet();
 2
3
4
5
6
7
8
在一个个方法上设置太麻烦,也可以在 @BaseRequest 注解中设置一整个接口类的SSL协议
@BaseRequest(sslProtocol = "TLS")
public interface SSLClient {
    @Get("https://localhost:5555/hello/user")
    String testSend();
}
 2
3
4
5
6
7
# 简单双向认证
若是需要在Forest中进行双向验证的HTTPS请求,也很简单。
在全局配置中添加keystore配置:
forest:
 ...
 ssl-key-stores:
   - id: keystore1           # id为该keystore的名称,必填
     file: test.keystore     # 公钥文件地址
     keystore-pass: 123456   # keystore秘钥
     cert-pass: 123456       # cert秘钥
     protocols: TLS          # SSL协议
 2
3
4
5
6
7
8
<forest:configuration>
    <forest:ssl-keystore
            id="keystore1"
            file="test.keystore"
            keystorePass="123456"
            certPass="123456"
            protocols="TLS"/>
</forest:configuration>
 2
3
4
5
6
7
8
// 获取Forest全局配置对象
configuration = Forest.config();
// 实例化 SSLKeyStore 对象,并输入参数
SSLKeyStore sslKeyStore = new SSLKeyStore(
    "keystore1",
    "ssl_client.keystore",
    "client",
    "456789",
    null,
    null);
// 注册 SSLKeyStore 对象
configuration.registerKeyStore(sslKeyStore);
 2
3
4
5
6
7
8
9
10
11
12
// Make sure to add code blocks to your code group
接着,在@Request中引入该keystore的id即可
@Get(url = "/user_info", keyStore = "keystore1")
ForestResponse<String> getUserInfo();
 2
另外,您也可以在全局配置中配多个keystore:
forest:
  ...
  ssl-key-stores:
    - id: keystore1          # 第一个keystore
      file: test1.keystore    
      keystore-pass: 123456  
      cert-pass: 123456      
      protocols: SSL       
    - id: keystore2          # 第二个keystore
      file: test2.keystore    
      keystore-pass: abcdef  
      cert-pass: abcdef      
      protocols: TLS       
      ...
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
随后在某个具体@Request中配置其中任意一个keystore的id都可以
# 更复杂的SSL验证
对于一些更复杂的SSL验证,光靠简单的配置无法完全应付,但在Forest中也可以通过自定义SSLSocketFactory和HostnameVerifier的方式提供更灵活的解决方案
# 自定义SSLSocketFactory
Forest提供了SSLSocketFactoryBuilder接口,通过实现该接口可以自定义SSLSocketFactory
/**
 * 自定义 SSLSocketFactory 构造器
 */
public class MySSLSocketFactoryBuilder implements SSLSocketFactoryBuilder {
    /**
     * 获取SSL Socket Factory
     */
    @Override
    public SSLSocketFactory getSSLSocketFactory(ForestRequest request, String protocol) throws Exception {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null,
                new TrustManager[] { new TrustAllManager() },
                new SecureRandom());
        return sslContext.getSocketFactory();
    }
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
然后通过@SSLSocketFactoryBuilder注解引入自定义的SSLSocketFactoryBuilder接口实现类
@Get("/user_info")
@SSLSocketFactoryBuilder(MySSLSocketFactoryBuilder.class)
ForestRequest<String> getUserInfo();
 2
3
# 自定义HostnameVerifier
在Forest中,也可以指定自定义的HostnameVerifier接口实现类
/**
 * 自定义主机名验证器
 */
public class MyHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String s, SSLSession sslSession) {
        System.out.println("do MyHostnameVerifier");
        if ("localhost".equals(s)) {
            return false;
        }
        return true;
    }
}
 2
3
4
5
6
7
8
9
10
11
12
13
然后通过@SSLHostnameVerifier注解引入自定义的主机名验证器类
@Get("/user_info")
@SSLHostnameVerifier(MyHostnameVerifier.class)
ForestRequest<String> getUserInfo();
 2
3