<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
spring.data.mongodb.uri=mongodb://adminUser:adminPass@localhost:27017/?authSource=admin&authMechanism=SCRAM-SHA-1 spring.data.mongodb.database=users
多个 IP 集群可以采用以下配置: spring.data.mongodb.uri=mongodb://user:pwd@ip1:port1,ip2:port2/databases
package com.wcf.mongo.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.wcf.mongo.entity.MongoBaseInfo;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author wangcanfeng
* @description 简单的mongodb使用接口
* @Date Created in 17:24-2019/3/20
*/
@Service
public class SimpleMongoServiceImpl<T extends MongoBaseInfo> implements SimpleMongoService<T> {
/**
* 注入template,减少重复代码
*/
@Autowired
private MongoTemplate mongoTemplate;
/**
* 功能描述: 创建一个集合
* 同一个集合中可以存入多个不同类型的对象,我们为了方便维护和提升性能,
* 后续将限制一个集合中存入的对象类型,即一个集合只能存放一个类型的数据
*
* @param name 集合名称,相当于传统数据库的表名
* @return:void
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 17:27
*/
@Override
public void createCollection(String name) {
mongoTemplate.createCollection(name);
}
/**
* 功能描述: 创建索引
* 索引是顺序排列,且唯一的索引
*
* @param collectionName 集合名称,相当于关系型数据库中的表名
* @param filedName 对象中的某个属性名
* @return:java.lang.String
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:13
*/
@Override
public String createIndex(String collectionName, String filedName) {
//配置索引选项
IndexOptions options = new IndexOptions();
// 设置为唯一
options.unique(true);
//创建按filedName升序排的索引
return mongoTemplate.getCollection(collectionName).createIndex(Indexes.ascending(filedName), options);
}
/**
* 功能描述: 获取当前集合对应的所有索引的名称
*
* @param collectionName
* @return:java.util.List<java.lang.String>
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:46
*/
@Override
public List<String> getAllIndexes(String collectionName) {
ListIndexesIterable<Document> list = mongoTemplate.getCollection(collectionName).listIndexes();
//上面的list不能直接获取size,因此初始化arrayList就不设置初始化大小了
List<String> indexes = new ArrayList<>();
for (Document document : list) {
document.entrySet().forEach((key) -> {
//提取出索引的名称
if (key.getKey().equals("name")) {
indexes.add(key.getValue().toString());
}
});
}
return indexes;
}
/**
* 功能描述: 往对应的集合中插入一条数据
*
* @param info 存储对象
* @param collectionName 集合名称
* @return:void
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:46
*/
@Override
public void insert(T info, String collectionName) {
mongoTemplate.insert(info, collectionName);
}
/**
* 功能描述: 往对应的集合中批量插入数据,注意批量的数据中不要包含重复的id
*
* @param infos 对象列表
* @return:void
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:47
*/
@Override
public void insertMulti(List<T> infos, String collectionName) {
mongoTemplate.insert(infos, collectionName);
}
/**
* 功能描述: 使用索引信息精确更改某条数据
*
* @param id 唯一键
* @param collectionName 集合名称
* @param info 待更新的内容
* @return:void
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 18:42
*/
@Override
public void updateById(String id, String collectionName, T info) {
Query query = new Query(Criteria.where("id").is(id));
Update update = new Update();
String str = JSON.toJSONString(info);
JSONObject jQuery = JSON.parseObject(str);
jQuery.forEach((key, value) -> {
//因为id相当于传统数据库中的主键,这里使用时就不支持更新,所以需要剔除掉
if (!key.equals("id")) {
update.set(key, value);
}
});
mongoTemplate.updateMulti(query, update, info.getClass(), collectionName);
}
/**
* 功能描述: 根据id删除集合中的内容
*
* @param id 序列id
* @param collectionName 集合名称
* @param clazz 集合中对象的类型
* @return:void
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:47
*/
@Override
public void deleteById(String id, Class<T> clazz, String collectionName) {
// 设置查询条件,当id=#{id}
Query query = new Query(Criteria.where("id").is(id));
// mongodb在删除对象的时候会判断对象类型,如果你不传入对象类型,只传入了集合名称,它是找不到的
// 上面我们为了方便管理和提升后续处理的性能,将一个集合限制了一个对象类型,所以需要自行管理一下对象类型
// 在接口传入时需要同时传入对象类型
mongoTemplate.remove(query, clazz, collectionName);
}
/**
* 功能描述: 根据id查询信息
*
* @param id 注解
* @param clazz 类型
* @param collectionName 集合名称
* @return:java.util.List<T>
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/20 16:47
*/
@Override
public T selectById(String id, Class<T> clazz, String collectionName) {
// 查询对象的时候,不仅需要传入id这个唯一键,还需要传入对象的类型,以及集合的名称
return mongoTemplate.findById(id, clazz, collectionName);
}
/**
* 功能描述: 查询列表信息
* 将集合中符合对象类型的数据全部查询出来
*
* @param collectName 集合名称
* @param clazz 类型
* @return:java.util.List<T>
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/21 10:38
*/
@Override
public List<T> selectList(String collectName, Class<T> clazz) {
return selectList(collectName, clazz, null, null);
}
/**
* 功能描述: 分页查询列表信息
*
* @param collectName 集合名称
* @param clazz 对象类型
* @param currentPage 当前页码
* @param pageSize 分页大小
* @return:java.util.List<T>
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/21 10:38
*/
@Override
public List<T> selectList(String collectName, Class<T> clazz, Integer currentPage, Integer pageSize) {
//设置分页参数
Query query = new Query();
//设置分页信息
if (!ObjectUtils.isEmpty(currentPage) && ObjectUtils.isEmpty(pageSize)) {
query.limit(pageSize);
query.skip(pageSize * (currentPage - 1));
}
return mongoTemplate.find(query, clazz, collectName);
}
/**
* 功能描述: 根据条件查询集合
*
* @param collectName 集合名称
* @param conditions 查询条件,目前查询条件处理的比较简单,仅仅做了相等匹配,没有做模糊查询等复杂匹配
* @param clazz 对象类型
* @param currentPage 当前页码
* @param pageSize 分页大小
* @return:java.util.List<T>
* @since: v1.0
* @Author:wangcanfeng
* @Date: 2019/3/21 10:48
*/
@Override
public List<T> selectByCondition(String collectName, Map<String, String> conditions, Class<T> clazz, Integer currentPage, Integer pageSize) {
if (ObjectUtils.isEmpty(conditions)) {
return selectList(collectName, clazz, currentPage, pageSize);
} else {
//设置分页参数
Query query = new Query();
query.limit(pageSize);
query.skip(currentPage);
// 往query中注入查询条件
conditions.forEach((key, value) -> query.addCriteria(Criteria.where(key).is(value)));
return mongoTemplate.find(query, clazz, collectName);
}
}
}
MongoDB’s implementation of SCRAM-SHA-1 represents an improvement in security over the previously-used MONGODB-CR, providing: * A tunable work factor (iterationCount), * Per-user random salts rather than server-wide salts, * A cryptographically stronger hash function (SHA-1 rather than MD5), and * Authentication of the server to the client as well as the client to the server.
MongoDB 3.0新增了一种认证机制(authenticationMechanisms) SCRAM-SHA-1, 并把他设置为默认的方式. 而Spring Boot里默认使用旧的认证机制. 这就造成了不一致从而认证通不过. 解决方法有两种:
(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更好之类的.
(2)改变认证方式, 就只能改java代码了 我们看一下Spring Boot的源码; 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方法既可.
首先创建一个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;
}
}
原文:https://www.cnblogs.com/yuarvin/p/14654863.html