博客有个好处,就是可以在涨人气的同时当个备忘录用。
最近有个需求,有一个远程的mongo数据库,里面有好几个集合,需要每天定时增量更新到本地数据库,同时要保证数据的安全性,防止中途被抓包暴露。
实现思路就是多数据源,设置定时器,主读从写,然后使用SSL加密。然而以前mongo用的很少,就是一个简单的CRUD功能,所以一开始就遇到很多坑。
首先在pom中导入依赖
org.springframework.boot
spring-boot-starter-data-mongodb
org.springframework.boot
spring-boot-configuration-processor
true
然后在application.properties中配置相关数据源
spring.data.mongodb.primary.host=远程数据库主机
spring.data.mongodb.primary.port=远程数据库端口
spring.data.mongodb.primary.database=远程数据库名称
spring.data.mongodb.primary.username=账号
spring.data.mongodb.primary.password=密码
spring.data.mongodb.secondary.host=127.0.0.1
spring.data.mongodb.secondary.port=27017
spring.data.mongodb.secondary.database=本地数据库名称
spring.data.mongodb.secondary.username=账号
spring.data.mongodb.secondary.password=密码
之后创建一个数据源抽象类
/*
数据源抽象类
*/
@Data
public abstract class AbstractMongoConfig {
private String host, database, username, password;
private int port;
public MongoDbFactory secMongoDbFactory() {
String url = "mongodb://"+username+":"+password+"@"+host+":"+port+"/"+database;
MongoClientURI uri = new MongoClientURI(url);
return new SimpleMongoDbFactory(new MongoClient(uri),database);
}
public abstract MongoTemplate getMongoTemplate() throws Exception;
}
设置主从数据源,从application.properties中读取连接信息
/*
配置远程数据库数据源
*/
@Configuration
@ConfigurationProperties(prefix = "spring.data.mongodb.primary")
public class PrimaryMongoConfig extends AbstractMongoConfig{
@Primary
@Override
public @Bean(name = "primaryMongoTemplate") MongoTemplate getMongoTemplate(){
return new MongoTemplate(priMongoDbFactory());
}
}
/*
配置本地数据库数据源
*/
@Configuration
@ConfigurationProperties(prefix = "spring.data.mongodb.secondary")
public class SecondaryMongoConfig extends AbstractMongoConfig {
@Override
public @Bean(name = "secondaryMongoTemplate") MongoTemplate getMongoTemplate(){
return new MongoTemplate(secMongoDbFactory());
}
}
为了方式SpringBoot自动注入mongotemplate,还要在启动类上增加内容
@SpringBootApplication(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) //排除自动注入mongoTemplate
使用时,指明要使用哪个数据源就可以了,如要对远程数据库操作,就使用primaryMongoTemplate。
在对mongo进行操作的使用,也可以使用实体类映射,但是要加上@Document注解,如果属性名和字段名不同,那么要加上@Field注解,如
@Data
@Document("test") // 对应集合名
public class BackUpEntity {
@Field("update_time") // 对应字段名
private Long updateTime;
}
然而mongo中可能有很多的内嵌文档等,有些复杂的表很难建立实体类映射,但是mongo简单在,可以使用json类型数据进行操作,不需要考虑实体类构造,简化代码如下。
@Repository
public class CommonRepository {
@Autowired
@Qualifier(value = "primaryMongoTemplate")
private MongoTemplate primaryMongoTemplate;
@Autowired
@Qualifier(value = "secondaryMongoTemplate")
private MongoTemplate secondaryMongoTemplate;
/*
查询远程数据库数据
*/
public JSONArray findSecondaryAll(String collectionName){
JSONArray json = JSONArray.parseArray(JSON.toJSONString(primaryMongoTemplate.findAll(String.class,collectionName)));
return json;
}
/*
备份远程数据库数据到本地
*/
public String backUpMongo(List
在本地数据库写入数据的时候要先将查询到的JSONArray数据转成List再传入
List
再写入的时候遇到了一个坑,报错信息
Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='root', source='ys_pbx_data_test', password=
, mechanismProperties= };
这是因为本地安装mongo的时候,并没有生成对应的管理员用户,所以要对数据库先生成管理员,赋予权限,然后在配置文件中填入账号密码,没有的话就会报错。
之后编写service层和controller层,调用repository,传入collectionName就可以了。
因为数据比较重要,所以要对其进行加密。mongo使用的是tcp传输,但是支持ssl加密。使用Wireshark进行抓包时,可以发现传输过程中的确都是明文。一开始理解错需求,使用了AES加密了接口返回数据,数据包还是明文的,不过也防止了查询接口直接暴露数据。
我的远程数据库是阿里云的,SSL配置可以参考:
官方文档:http://mongodb.github.io/mongo-java-driver/3.0/driver/reference/connecting/ssl/
阿里云:https://www.alibabacloud.com/help/zh/doc-detail/89245.htm?spm=a2c63.p38356.879954.6.5e4261d9OMoRgd#concept-v1g-vyv-y2b
https://www.alibabacloud.com/help/zh/doc-detail/89276.html
具体过程如下
第一步是改造远程数据库的数据源配置,新建一个配置类
@Configuration
public class MongoSSLConfig {
@Bean
public static MongoClient createNetworkMongoClient() {
MongoCredential credentials = getCredentials();
String host = "主机名";
int port = "端口";
List addrs = Collections.singletonList(new ServerAddress(host, port));
System.setProperty("javax.net.ssl.trustStore","证书路径,注意使用jks文件");
System.setProperty("javax.net.ssl.trustStorePassword","信任库密码");
MongoClientOptions options = MongoClientOptions.builder()
.sslEnabled(true) //开启SSL连接
.sslInvalidHostNameAllowed(true) //不检查证书域名
.build();
return new MongoClient(addrs, credentials, options);
}
private static MongoCredential getCredentials() {
String username = "用户名";
String database = "数据库名";
String pass = "密码";
char[] password = pass.toCharArray();
return MongoCredential.createCredential(username, database, password);
}
}
修改 数据源抽象类 配置远程数据库新连接
public MongoDbFactory priMongoDbFactory() {
MongoClient mongoClient = MongoSSLConfig.createNetworkMongoClient();
return new SimpleMongoDbFactory(mongoClient,database);
}
在primaryMongoConfig中调用新方法
public @Bean(name = "primaryMongoTemplate") MongoTemplate getMongoTemplate(){
return new MongoTemplate(priMongoDbFactory());
}
第二步就是生成远程数据库的信任证书并下载到本地。
第三步将证书入本机信赖库,具体操作可以看官方文档,并将jks证书路径填入上面的代码中,如果使用pem等格式证书可能报错。
最后重启项目,可以正常运作,再次抓包,发现明文数据已经被加密。