正好做Mongodb主從復制嘗試使用Spring Boot Data Mongodb Starter插件鏈接訪問Mongodb數據庫集群。
遇到的坑:
- spring.data.mongodb.host和spring.data.mongodb.port形式不適合集群配置,會報host無法識別異常
- spring.data.mongodb.uri中經常拋出authentication failed異常
解決辦法:
- 對于第一個坑,請使用spring.data.mongodb.uri。如果使用了uri,則其余的host/username/password/db/auth-db這些全部無效。
- 對于第二個坑,請在spring.data.mongodb.uri中指定replicaSet和authsource,另外記得把所有集群節點服務器地址都列全。
如果auth-db和db是同一個,則無需加authsource,如果不同,則加authsource=admin
我沒有把authsource指定,所以一直報authentication failed異常。然后只好一點點去發掘問題點,最后查到在com.mongodb.ConnectionString類中的createCredentials中
private MongoCredential createCredentials(final Map<String, List<String>> optionsMap, final String userName,
final char[] password) {
AuthenticationMechanism mechanism = null;
String authSource = (database == null) ? "admin" : database;
String gssapiServiceName = null;
String authMechanismProperties = null;
for (final String key : AUTH_KEYS) {
String value = getLastValue(optionsMap, key);
if (value == null) {
continue;
}
if (key.equals("authmechanism")) {
mechanism = AuthenticationMechanism.fromMechanismName(value);
} else if (key.equals("authsource")) {
authSource = value;
} else if (key.equals("gssapiservicename")) {
gssapiServiceName = value;
} else if (key.equals("authmechanismproperties")) {
authMechanismProperties = value;
}
}
MongoCredential credential = null;
if (mechanism != null) {
switch (mechanism) {
case GSSAPI:
credential = MongoCredential.createGSSAPICredential(userName);
if (gssapiServiceName != null) {
credential = credential.withMechanismProperty("SERVICE_NAME", gssapiServiceName);
}
break;
case PLAIN:
credential = MongoCredential.createPlainCredential(userName, authSource, password);
break;
case MONGODB_CR:
credential = MongoCredential.createMongoCRCredential(userName, authSource, password);
break;
case MONGODB_X509:
credential = MongoCredential.createMongoX509Credential(userName);
break;
case SCRAM_SHA_1:
credential = MongoCredential.createScramSha1Credential(userName, authSource, password);
break;
default:
throw new UnsupportedOperationException(format("The connection string contains an invalid authentication mechanism'. "
+ "'%s' is not a supported authentication mechanism",
mechanism));
}
} else if (userName != null) {
credential = MongoCredential.createCredential(userName, authSource, password);
}
if (credential != null && authMechanismProperties != null) {
for (String part : authMechanismProperties.split(",")) {
String[] mechanismPropertyKeyValue = part.split(":");
if (mechanismPropertyKeyValue.length != 2) {
throw new IllegalArgumentException(format("The connection string contains invalid authentication properties. "
+ "'%s' is not a key value pair", part));
}
String key = mechanismPropertyKeyValue[0].trim().toLowerCase();
String value = mechanismPropertyKeyValue[1].trim();
if (key.equals("canonicalize_host_name")) {
credential = credential.withMechanismProperty(key, Boolean.valueOf(value));
} else {
credential = credential.withMechanismProperty(key, value);
}
}
}
return credential;
}
authSource默認會指向我們目標數據的數據庫。然而在身份驗證機制中我們通常需要指向admin。(非常想報粗口,代碼作者在這里腦袋被men擠了么)。所以需要強制指定authSource中指定。具體指定方式如下:
mongodb://{用戶名}:{密碼}@{host1}:27017,{host2}:27017,{host3}:27017/{目標數據庫}?replicaSet={復制集名稱}&write=1&readPreference=primary&authsource={授權數據庫}