扩展Nacos使其支持https和登陆 拉取配置 com.alibaba.nacos.client.naming.tls.enable

官方依赖版本:

nacos-server :1.2.1
nacos-config-spring-boot.version :0.2.7
nacos server支持启用鉴权
### If turn on auth system:
nacos.core.auth.enabled=true
按照官方example( https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-config-example )配置好 发现客户端连接不上,总是403的错误,根据惯例来讲,这是没有授权引起的,然后我去github上找了下,果然有人提了这个 issue
扩展Nacos使其支持https和登陆 拉取配置 com.alibaba.nacos.client.naming.tls.enable 扩展Nacos使其支持https和登陆 拉取配置 com.alibaba.nacos.client.naming.tls.enable
 
问题有了,那么接下来追踪产生的原因。
 
跟踪源码发现 login 方法调用HttpClient 而 nacos server的数据接口是用ServerHttpAgent->HttpSimpleClient ,都是对HttpConnection的包装,但内部url拼接规则不同
扩展Nacos使其支持https和登陆 拉取配置 com.alibaba.nacos.client.naming.tls.enable
 
由于这里拼接了“/”,导致 nacos上下文配置拼接时路径变成了//
nacos.config.context-path=/nacos
nacos server端使用了spring security进行权限,在新版的spring security中,对url进行了规则强校验,不允许类似“http://127.0.0.1//xxx”这种"//“出现,导致出现获取配置出现500的错误。(如果设置nacos.config.context-path=nacos 又会导致登陆失败,拿不到accessToken)
 
所以如果要不该源码实现 登陆权限 拉取配置需要 通过代理转换请求地址。
于是在nacos server端配置nginx 代理请求
扩展Nacos使其支持https和登陆 拉取配置 com.alibaba.nacos.client.naming.tls.enable
 
在访问nacos前加入一层nginx
location / {
          proxy_pass http://127.0.0.1:8848/;
        }
    location /n/nacos {
          proxy_pass http://127.0.0.1:8848/nacos/;
        }
这样做的目的是为了重写”//“为”/",保证security的url校验能够成功,
其中 nacosserver是nacos的访问url,这里我配置了两个,第一个是访问nacos的管理界面,第二个是client注册nacos的url
 
这样登陆授权是满足了,http明文传输配置中心的密码等敏感信息还是不够安全,所以扩展nacos使其支持https就是问题了
由于ServerHttpAgent类源码写死了 isSSL=false,看来不该源码不行了。(本来想替换ClientWorker的agent实例,结果发现是
NacosConfigService New出来的,和Spring框架设计上的差距有点大啊————)
 
好吧,源码开撸>>>>
修改ServerHttpAgent
private String getUrl(String serverAddr, String relativePath,boolean isSSL) {
    String contextPath = serverListMgr.getContentPath().startsWith("/") ?
        serverListMgr.getContentPath() : "/" + serverListMgr.getContentPath();
    String url= StringUtils.removeEnd(serverAddr,"/")+"/"+StringUtils.removeStart(contextPath,"/")+ relativePath;
    if (isSSL &&url.startsWith(httpPre)){
        return httpsPre+StringUtils.removeStart(url,httpPre);
    }else{
        return url;
    }
}
SecurityProxy.java
if (HttpClient.ENABLE_HTTPS){
    url = "https://" + server + contextPath + LOGIN_URL;
}else {
    url = "http://" + server + contextPath + LOGIN_URL;
}

if (server.contains(Constants.HTTP_PREFIX)||server.contains(Constants.HTTPS_PREFIX)) {
    url = server + contextPath + LOGIN_URL;
}
为了避免证书 校验 请求域名的问题 对HttpConnection所在类做了以下处理
static {
    try {
        trustAllHttpsCertificates();
        HttpsURLConnection.setDefaultHostnameVerifier
(
                (urlHostName, session) -> true
            );
    } catch (Exception e) {
    }
}

private static void trustAllHttpsCertificates()
    throws NoSuchAlgorithmException, KeyManagementException {
    TrustManager[] trustAllCerts = new TrustManager[1];
    trustAllCerts[0] = new TrustAllManager();
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, null);
    HttpsURLConnection.setDefaultSSLSocketFactory(
        sc.getSocketFactory());
}

private static class TrustAllManager
    implements X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    public void checkServerTrusted(X509Certificate[] certs,
                                   String authType) {
    }

    public void checkClientTrusted(X509Certificate[] certs,
                                   String authType) {
    }
}
为了避免配置过于杂乱,对于是否启用ssl依然复用 这个属性
com.alibaba.nacos.client.naming.tls.enable
启动类加System.setProperty("com.alibaba.nacos.client.naming.tls.enable","true")或者  启动命令加-Dcom.alibaba.nacos.client.naming.tls.enable
 
完整代码参见我的github  https://github.com/huawenyao/nacos ,欢迎start~
 
关键源码
ServerHttpAgent.httpGet
private String getUrl(String serverAddr, String relativePath) {
return serverAddr + "/" + serverListMgr.getContentPath() + relativePath;
}
 
public NacosConfigService(Properties properties) throws NacosException {
String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
if (StringUtils.isBlank(encodeTmp)) {
encode = Constants.ENCODE;
} else {
encode = encodeTmp.trim();
}
initNamespace(properties);
agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
agent.start();
worker = new ClientWorker(agent, configFilterChainManager, properties);
}