Spring Cloud 自定义ConfigServer
公司需要将系统配置信息中的敏感信息独立存放。
现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求:
(1)数据库配置信息分离(主要是Db信息)。
(2)原有Config Server功能继续可用。
(3)对特定环境(这里是仿真环境-Demo、生产环境)可以定制配置信息。
思路有如下几种:
(1)Spring Aop 拦截Config Server中配置返回方法,改写方法返回值。
(2)Spring Aop 拦截Config Server中读取配置方法,读取更多文件源。
(3)Filter拦截Config Server中数据返回方法,改写方法返回值。
其中:
方法1与方法3都是对返回结果进行处理,可以兼容Spring Cloud Config的版本升级,因方法1需要绑定到特定方法,而方法3无需考虑具体方法,耦合性更低。
方法2需要改写Config Server的代码,或覆盖Config Server的Bean,较1和2复杂,且无法随这主版本升级,直接pass。
综合考虑采用方法3,以下是处理与代码:
公司统一标准,将Db配置信息存储到Json文件中,因此需要一个解析Json文件的Service:
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.core.env.MapPropertySource;
- import org.springframework.stereotype.Component;
- import java.io.File;
- import java.io.IOException;
- import java.util.Date;
- import java.util.LinkedHashMap;
- import java.util.Map;
- @Component
- public class JsonSourcePropertiesLoader {
- @Value("${Config.Json.FileLocation}")
- String FileLocation;
- public MapPropertySource load() throws IOException {
- File jsonFile = new File(FileLocation);
- final Map<String, Object> source = process(jsonFile);
- System.out.println(new Date().getTime());
- return new MapPropertySource("dbConfigMap",source);
- }
- private Map<String, Object> process(final File resourceFile){
- Map<String, Object> map = null;
- try{
- map = new ObjectMapper().readValue(resourceFile, LinkedHashMap.class);
- }catch (IOException e){
- e.printStackTrace();
- }
- return map;
- }
- }
需要一个类拦截并获取Config Server在正常处理请求后返回的内容,以便接下来的改写:
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpServletResponseWrapper;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.PrintWriter;
- public class MyResponseWrapper extends HttpServletResponseWrapper {
- private ByteArrayOutputStream byteArrayOutputStream;
- private ServletOutputStream servletOutputStream;
- private PrintWriter printWriter;
- public MyResponseWrapper(HttpServletResponse response) {
- super(response);
- byteArrayOutputStream = new ByteArrayOutputStream(4096);
- printWriter = new PrintWriter(byteArrayOutputStream);
- servletOutputStream = new MyServletOutputStream(byteArrayOutputStream);
- }
- @Override
- public PrintWriter getWriter() throws IOException{
- return printWriter;
- }
- @Override
- public ServletOutputStream getOutputStream() throws IOException{
- return servletOutputStream;
- }
- @Override
- public void flushBuffer() throws IOException{
- if(printWriter!=null){
- printWriter.flush();
- }
- if(servletOutputStream !=null){
- servletOutputStream.flush();
- }
- }
- @Override
- public void reset(){
- byteArrayOutputStream.reset();
- }
- public byte[] getResult(){
- try{
- flushBuffer();
- }catch (IOException e){
- }
- return byteArrayOutputStream.toByteArray();
- }
- }
对ServletOutputStream的封装类:
- import javax.servlet.ServletOutputStream;
- import javax.servlet.WriteListener;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- public class MyServletOutputStream extends ServletOutputStream {
- ByteArrayOutputStream output;
- public MyServletOutputStream(ByteArrayOutputStream output) {
- this.output = output;
- }
- @Override
- public void write(int b) throws IOException {
- output.write(b);
- }
- public void write(byte[] data, int offset, int length) {
- output.write(data, offset, length);
- }
- @Override
- public boolean isReady() {
- return false;
- }
- @Override
- public void setWriteListener(WriteListener listener) {
- }
- }
有了拦截与处理的方法,最后就是把功能组合到拦截器中:
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONArray;
- import com.alibaba.fastjson.JSONObject;
- import com.fang.microservice.cloud.Loader.JsonSourcePropertiesLoader;
- import com.fang.microservice.cloud.Wrapper.MyResponseWrapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.web.servlet.ServletComponentScan;
- import org.springframework.core.env.MapPropertySource;
- import org.springframework.stereotype.Component;
- import javax.servlet.*;
- import javax.servlet.annotation.WebFilter;
- import javax.servlet.http.HttpServletResponse;
- import java.io.*;
- @Component
- @ServletComponentScan
- @WebFilter(urlPatterns = "/{name}/{profiles:.*[^-].*}", filterName = "ConfigServletFilter")
- public class ConfigAspectFilter implements Filter {
- @Autowired
- JsonSourcePropertiesLoader jsonConfig;
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- MyResponseWrapper wrapper = new MyResponseWrapper((HttpServletResponse) response);
- chain.doFilter(request, wrapper);
- byte[] respBytes = wrapper.getResult();
- String result = Byte2String(respBytes);
- if (response.getContentType().toLowerCase().contains("application/json")) {
- if (!result.toLowerCase().contains("datasource.mysql.")) {
- JSONObject configObj = (JSONObject) JSON.parse(result);
- JSONArray propertySourcesObjs = configObj.getJSONArray("propertySources");
- if (propertySourcesObjs.size() > 0) {
- JSONObject propertySourcesObj = (JSONObject) propertySourcesObjs.get(0);
- JSONObject sourceObj = propertySourcesObj.getJSONObject("source");
- MapPropertySource mps = jsonConfig.load();
- for (String key : mps.getPropertyNames()) {
- sourceObj.put(key, mps.getProperty(key));
- }
- result = JSON.toJSONString(configObj);
- }
- }
- }
- response.setContentLength(-1);
- PrintWriter out = response.getWriter();
- out.write(result);
- out.flush();
- out.close();
- }
- public static String Byte2String(byte[] buff) {
- ByteArrayOutputStream result = new ByteArrayOutputStream();
- int length;
- try {
- result.write(buff, 0, buff.length);
- } catch (Exception e) {
- e.printStackTrace();
- }
- try {
- return result.toString("UTF-8");
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return "";
- }
- @Override
- public void destroy() {
- }
- }
作为敏感数据存储的文件内容:
- { "testDbName":"testDb","userName":"userName","password":"pass"}
测试效果如下:
最后不能忘记特定敏感文件的路径,需要配置在配置文件中:
Config.Json.FileLocation=/xx/test/test.json
这样,一个既可以规避敏感文件,又能不失Config Server方便性的简单处理方案就这样可以投入使用了。
Spring Cloud 自定义ConfigServer的更多相关文章
- Spring Cloud 自定义ConfigServer 解决敏感信息存储问题
公司需要将系统配置信息中的敏感信息独立存放. 现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求: (1)数据库配置信息分离(主要是Db信息). ...
- spring cloud 自定义ribbon客户端
一.自定义Ribbon客户端-[方式一]配置类 1.1.自定义负载规则 增加RibbonConfiguration.java配置类 public class RibbonConfiguration { ...
- Spring Cloud教程合集
Spring Cloud系列终于搞完啦! 这一系列是笔者的学习笔记,原书之前也给小伙伴们推荐过 <Spring Cloud微服务实战> 原书采用了较老的Brixton版,笔者在学习的过程中 ...
- spring cloud 2.x版本 Config配置中心教程
前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前面的文章eureka-server的实现. 参考 eureka-server ...
- Spring Cloud 升级之路 - 2020.0.x - 7. 使用 Spring Cloud LoadBalancer (2)
本项目代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们使用 Spri ...
- SpringCloud升级之路2020.0.x版-24.测试Spring Cloud LoadBalancer
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 通过单元测试,我们 ...
- spring cloud 服务A调用服务B自定义token消失,记录
后端:spring cloud 前端:vue 场景:前端ajax请求,包装自定义请求头token到后台做验证,首先调用A服务,A服务通过Feign调用B服务发现自定义token没有传到B服务去; 原因 ...
- Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑
应用场景 上一篇<Spring Cloud Stream消费失败后的处理策略(一):自动重试>介绍了默认就会生效的消息重试功能.对于一些因环境原因.网络抖动等不稳定因素引发的问题可以起到比 ...
- Spring Cloud(7):Zuul自定义过滤器和接口限流
上文讲到了Zuul的基本使用: https://www.cnblogs.com/xuyiqing/p/10884860.html 自定义Zuul过滤器: package org.dreamtech.a ...
随机推荐
- Linux中常见问题(磁盘 定时任务)
第1章 linux无法上网 1) 第一步,先ping域名. ping www.baidu.com 2)再ping一个公网ip , ping 223.5.5.5/223.6.6.6/114.11 ...
- Azkaban安装部署
在root的用户下搭建的 • Azkaban安装部署(可参照:http://azkaban.github.io/azkaban/docs/latest/) 1):前提 安装JDK,安装Hadoop,H ...
- 深入分析Android动画(一)
动画的分类: ①View动画 View动画顾名思义其作用对象为View,包含平移.缩放.旋转.透明,这四类变化分别对应着Animation的子类TranlateAnimation.ScaleAnima ...
- 外键删除(T-SQL Drop Foreign Key)
列出某张表相关的 FK Name select distinct name from sys.objects where object_id in ( select fk.constraint_o ...
- 分享:苹果APP更新上架被拒的另一种理由(Safety - Objectionable Content)
这两个星期,本来想和大伙分享:写IT连创业系列运营篇. 但时间飞过,仍只是写了开头,一直很忙,没能完往下写. 今天就动手写点其它内容,哈哈,免的和小伙伴太陌生〜〜〜 前几天更新了:IT恋和IT连的版本 ...
- python之optparse模块
测试例子 #!/usr/bin/env python2.7 import sys import os from optparse import OptionParser def parse_optio ...
- MySQL长短密码
MySQL长短密码 今天批量搭建MySQL环境的时候,遇到长短密码问题,故就此问题总结一下长短密码. 介绍 1.长密码例子: mysql> show grants for 'test'@'loc ...
- JSF页面中使用js函数回调后台action方法
最近遇到了一个问题就是在JSF页面中嵌入html页面,这个html页面中很多功能是使用js动态生成的,现在需要在js函数里想去调用JSF中action类method()方法并动态传送数据给后台进行处理 ...
- STM32F030如何正确配置IO口的复用功能
本文所使用的单片机型号为STM32F030C8T6. 在030系列的单片机中,PA2引脚除了作为普通的IO引脚用作输入输出功能以外,还可以作为内部外设串口1,串口2,定时器15通道1这三个外设的功能引 ...
- Redis 快速入门
Redis 快速入门 谈到Redis,大家应该都不陌生.它是用c语言开发的一个高性能键值数据库,主要用于缓存领域.本章通过Redis的安装,Redis的五大数据类型,Redis的Java客户端,Red ...