Spring Boot中使用 MongoDB 3.0
mongodb3.0出来了,有多了很多新特性.不过这不是我要说的
.
如果要在Java应用程序里用mongodb3.0你得用3.0的Java驱动程序.旧的是不行的.
然而很多Java的库还没有跟上脚步,用的是2.6的驱动程序.SpringBoot就是其中之一.
我相信过不了多久,SpringBoot就会升级支持3.0.那么现在如果想要用mongodb3.0怎办呢?
首先你需要把maven的依赖改成3.0相关的版本:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.7.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>1.10.0.RELEASE</version>
</dependency>然后以SpringBoot为例,你要配置mongodb连接的参数(application.yaml)
spring:
data:
mongodb:
host: localhost
port: 27017
username: user
password: abc123
database: zen
authenticationDatabase: zen我在mongodb3.0里已经创建了一个数据库叫zen,并且给zen创建了一个用户user,密码是abc123
然后我设置mongodb的auth=true
在2.6开始可以用yaml的方式配置:
security:
authorization: enabled按照SpringBoot的文档,有了以上配置后,你就可以使用Mongodb了,比如用MongodbTemplate访问数据库.而实际上如果是3.0那么数据库会告诉你认证失败.
这个问题困扰了很久,最后还是查看SpringBoot的源码才发现,原来MongoDB3.0新增了一种认证机制(authenticationMechanisms)SCRAM-SHA-1,并把他设置为默认的方式.而SpringBoot里默认使用旧的认证机制.这就造成了不一致从而认证通不过.
解决方法有两种:
1.把Mongodb的认证机制改了:
mongodb支持如下几种:
SCRAM-SHA-1 MONGODB-CR MONGODB-X509 GSSAPI (Kerberos) PLAIN (LDAP SASL)
把Mongodb的认证方式改变一下自然能解决问题.可以同时支持多个.
setParameter:
authenticationMechanisms: MONGODB-CR,SCRAM-SHA-1
enableLocalhostAuthBypass: false
logLevel: 4但是既然MongoDB从3.0开始用SCRAM-SHA-1作为默认,应该是有道理的,比如安全性方面比MONGODB-CR更好之类的.
MongoDB’simplementationofSCRAM-SHA-1representsanimprovementinsecurityoverthepreviously-usedMONGODB-CR,providing:
*Atunableworkfactor(iterationCount),
*Per-userrandomsaltsratherthanserver-widesalts,
*Acryptographicallystrongerhashfunction(SHA-1ratherthanMD5),and
*Authenticationoftheservertotheclientaswellastheclienttotheserver.
2.如果你不行改变认证方式,就只能改java代码了
我们看一下SpringBoot的源码;
org.springframework.boot.autoconfigure.mongo.MongoProperties的createMongoClient方法:
public MongoClient createMongoClient(MongoClientOptions options)
throws UnknownHostException {
try {
if (hasCustomAddress() || hasCustomCredentials()) {
if (options == null) {
options = MongoClientOptions.builder().build();
}
List<MongoCredential> credentials = null;
if (hasCustomCredentials()) {
String database = this.authenticationDatabase == null ? getMongoClientDatabase()
: this.authenticationDatabase;
credentials = Arrays.asList(MongoCredential.createMongoCRCredential(
this.username, database, this.password));
}
String host = this.host == null ? "localhost" : this.host;
int port = this.port == null ? DEFAULT_PORT : this.port;
return new MongoClient(Arrays.asList(new ServerAddress(host, port)),
credentials, options);
}
// The options and credentials are in the URI
return new MongoClient(new MongoClientURI(this.uri, builder(options)));
}
finally {
clearPassword();
}
}可以看到它是用MongoCredential.createMongoCRCredential方法来创建认证信息,并且没有留出任何公开的接口让你改变这一行为.这个真是不应该呀.
找到问题所在,其实解决就非常方便了,使用createScramSha1Credential方法既可.
但是就像上面说的,没有公开接口,只能把MongoProperties复制一份,然后改这一行代码了
首先创建一个MongoDBConfiguration类,用于创建MongoClient实例.
@Configuration
@EnableConfigurationProperties(MongoProperties.class)
public class MongoDBConfiguration {
@Autowired
private MongoProperties properties;
@Autowired(required = false)
private MongoClientOptions options;
private Mongo mongo;
@PreDestroy
public void close() {
if (this.mongo != null) {
this.mongo.close();
}
}
@Bean
public Mongo mongo() throws UnknownHostException {
this.mongo = this.properties.createMongoClient(this.options);
return this.mongo;
}
}细心的程序猿们会发现一个问题:SpringBoot中不是有一个一模一样的类吗?是啊.但是没法直接用那个类(在org.springframework.boot.autoconfigure.mongo包),因为它里面用的是同一个包下的MongoProperties,而不会使用你的.
然后就是我们自己的MongoProperties类了,我就不写了,就上面那一句话不一样,还有就是要让MongoDBConfiguration引用我们自己的MongoProperties类.
然后就OK了.
其实如果SpringBoot做的完善一点应该提供可以选择认证机制的功能,或者提供公开接口实现自己的认证功能