官网配置
前言
由于以前使用mongo都是简单的本地裸奔,根本没有设置过用户密码,昨天收到运维同事反馈说服务器被挖矿木马攻击了,吓得赶紧在docker里面开启认证,但是设置完后问题来了,开发同事又说不能用配置文件如下
spring:
data:
mongodb:
host: 127.0.0.1
port: 27017
username: sunny
password: 123456
database: admin
看起来很ok但是在启动boot的时候在会连接两次(别问我为什么是两次,boot源码还没有看多少),第一次是失败了,但是第二次成功了,但是读取数据时候又用的是mongoTemplate
(第一次创建的)这个导致一直卡死,没有办法只能用uri
方式配置了,下面介绍boot怎么解析它
配置文件(yml)
spring:
data:
mongodb:
uri: mongodb://sunny:[email protected]:27017/file
对应的java代码是
@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoProperties {
public static final String DEFAULT_URI = "mongodb://localhost/test";
// 默认uri=
public String determineUri() {
return (this.uri != null ? this.uri : DEFAULT_URI);
}
....
// 关键代码 idea ctrl+g 169 就可以看到
public String getMongoClientDatabase() {
if (this.database != null) {
return this.database;
}
return new MongoClientURI(determineUri()).getDatabase();
}
}
// 读取yml文件前缀为(spring.data.mongodb)
由源码可以看出这里如果uri=null则使用DEFAULT_URI
,然后进入MongoClientURI
类
public class MongoClientURI {
private final ConnectionString proxied;
private final MongoClientOptions.Builder builder;
/**
* Creates a MongoURI from the given string.
* 从给定的字符串创建一个MongoURI
* @param uri the URI
*/
public MongoClientURI(final String uri) {
this(uri, new MongoClientOptions.Builder());
}
/**
* Mongo database URI. Cannot be set with host, port and credentials.
*/
public MongoClientURI(final String uri, final MongoClientOptions.Builder builder) {
this.builder = notNull("builder", builder);
proxied = new ConnectionString(uri);
}
}
到这里了就先看new MongoClientOptions.Builder()
方法
@Immutable
public class MongoClientOptions {
/**
* Creates a Builder for MongoClientOptions, getting the appropriate system properties *for initialization.
* 这里MongoClientOptions创建一个构建器,并且设置一些系统属性
*/
public Builder() {
/*设置心跳频率,在集群中*/ heartbeatFrequency(Integer.parseInt(System.getProperty("com.mongodb.updaterIntervalMS", "10000")));
/*最小心跳频率*/ minHeartbeatFrequency(Integer.parseInt(System.getProperty("com.mongodb.updaterIntervalNoMasterMS", "500")));
/*设置用于群集心跳的连接的连接超时*/ heartbeatConnectTimeout(Integer.parseInt(System.getProperty("com.mongodb.updaterConnectTimeoutMS", "20000")));
/*socket 链接超时*/ heartbeatSocketTimeout(Integer.parseInt(System.getProperty("com.mongodb.updaterSocketTimeoutMS", "20000")));
/*可接受的延迟差 15秒*/ localThreshold(Integer.parseInt(System.getProperty("com.mongodb.slaveAcceptableLatencyMS", "15")));
}
}
System.getProperty(xxxx)可以在jvm里面设置或者直接在boot启动方法里面设置。
看到这里后发现使用了一个proxied(代理)继续往下看
public class ConnectionString {
/**
* Creates a ConnectionString from the given string.
*
* @param connectionString the connection string
* @since 3.0
*/
public ConnectionString(final String connectionString) {
this.connectionString = connectionString;
//这里可以看出支持两种协议:mongodb://和mongodb+srv://
boolean isMongoDBProtocol = connectionString.startsWith(MONGODB_PREFIX);
boolean isSRVProtocol = connectionString.startsWith(MONGODB_SRV_PREFIX);
if (!isMongoDBProtocol && !isSRVProtocol) {
throw new IllegalArgumentException(format("The connection string is invalid. "
+ "Connection strings must start with either '%s' or '%s", MONGODB_PREFIX, MONGODB_SRV_PREFIX));
}
// 截取协议头,eg:sunny:[email protected]:27007/file?connectTimeoutMS=400000
String unprocessedConnectionString;
if (isMongoDBProtocol) {
unprocessedConnectionString = connectionString.substring(MONGODB_PREFIX.length());
} else {
unprocessedConnectionString = connectionString.substring(MONGODB_SRV_PREFIX.length());
}
// 分割出用户和主机信息 eg:sunny:[email protected]:27007
String userAndHostInformation;
int idx = unprocessedConnectionString.lastIndexOf("/");
if (idx == -1) {
if (unprocessedConnectionString.contains("?")) {
throw new IllegalArgumentException("The connection string contains options without trailing slash");
}
userAndHostInformation = unprocessedConnectionString;
unprocessedConnectionString = "";
} else {
//userAndHostInformation=sunny:[email protected]:27007
userAndHostInformation = unprocessedConnectionString.substring(0, idx);
// unprocessedConnectionString=file?connectTimeoutMS=400000
unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
}
// Split the user and host information
String userInfo;
String hostIdentifier;
String userName = null;
char[] password = null;
idx = userAndHostInformation.lastIndexOf("@");
if (idx > 0) {
// userInfo=sunny:123456
userInfo = userAndHostInformation.substring(0, idx);
// hostIdentifier=127.0.0.1:27007
hostIdentifier = userAndHostInformation.substring(idx + 1);
// 判断userInfo是否包含@和:出现次数大于1
int colonCount = countOccurrences(userInfo, ":");
if (userInfo.contains("@") || colonCount > 1) {
throw new IllegalArgumentException("The connection string contains invalid user information. "
+ "If the username or password contains a colon (:) or an at-sign (@) then it must be urlencoded");
}
if (colonCount == 0) {
userName = urldecode(userInfo);
} else {
// idx=5
idx = userInfo.indexOf(":");
// userName=sunny
userName = urldecode(userInfo.substring(0, idx));
//passpword=123456
password = urldecode(userInfo.substring(idx + 1), true).toCharArray();
}
} else {
hostIdentifier = userAndHostInformation;
}
// 验证host
List unresolvedHosts = unmodifiableList(parseHosts(asList(hostIdentifier.split(",")), isSRVProtocol));
this.hosts = isSRVProtocol ? resolveHostFromSrvRecords(unresolvedHosts.get(0)) : unresolvedHosts;
// 解析参数
String nsPart;
idx = unprocessedConnectionString.indexOf("?");
if (idx == -1) {
nsPart = unprocessedConnectionString;
unprocessedConnectionString = "";
} else {
// nsPart=file 获取数据库名称
nsPart = unprocessedConnectionString.substring(0, idx);
// unprocessedConnectionString=connectTimeoutMS=400000
unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
}
if (nsPart.length() > 0) {
nsPart = urldecode(nsPart);
idx = nsPart.indexOf(".");
// 是否指定集合
if (idx < 0) {
database = nsPart;
collection = null;
} else {
database = nsPart.substring(0, idx);
collection = nsPart.substring(idx + 1);
}
} else {
database = null;
collection = null;
}
/*数据库健康链接 mongodb+srv 协议才使用*/
String txtRecordsQueryParameters = isSRVProtocol ? resolveAdditionalQueryParametersFromTxtRecords(unresolvedHosts.get(0)) : "";
/*链接参数 connectTimeoutMS=400000*/
String connectionStringQueryParamenters = unprocessedConnectionString;
Map> connectionStringOptionsMap = parseOptions(connectionStringQueryParamenters);
Map> txtRecordsOptionsMap = parseOptions(txtRecordsQueryParameters);
//判断是否设置 authsource、replicaset
if (!ALLOWED_OPTIONS_IN_TXT_RECORD.containsAll(txtRecordsOptionsMap.keySet())) {
throw new MongoConfigurationException(format("A TXT record is only permitted to contain the keys %s, but the TXT record for "
+ "'%s' contains the keys %s", ALLOWED_OPTIONS_IN_TXT_RECORD, unresolvedHosts.get(0), txtRecordsOptionsMap.keySet()));
}
Map> combinedOptionsMaps = combineOptionsMaps(txtRecordsOptionsMap, connectionStringOptionsMap);
if (isSRVProtocol && !combinedOptionsMaps.containsKey("ssl")) {
combinedOptionsMaps.put("ssl", singletonList("true"));
}
translateOptions(combinedOptionsMaps);
credential = createCredentials(combinedOptionsMaps, userName, password);
warnOnUnsupportedOptions(combinedOptionsMaps);
}
}
完结
mongo这块代码是临时看的,可能没有太认真,如果有误请指出,还有就是mongodb+srv 协议没有使用过,有使用经验的欢迎留言,谢谢