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做的完善一点应该提供可以选择认证机制的功能,或者提供公开接口实现自己的认证功能

相关推荐