公司需要将系统配置信息中的敏感信息独立存放。

现有系统采用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:

  1. import com.fasterxml.jackson.databind.ObjectMapper;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.core.env.MapPropertySource;
  4. import org.springframework.stereotype.Component;
  5.  
  6. import java.io.File;
  7. import java.io.IOException;
  8. import java.util.Date;
  9. import java.util.LinkedHashMap;
  10. import java.util.Map;
  11.  
  12. @Component
  13. public class JsonSourcePropertiesLoader {
  14. @Value("${Config.Json.FileLocation}")
  15. String FileLocation;
  16.  
  17. public MapPropertySource load() throws IOException {
  18. File jsonFile = new File(FileLocation);
  19. final Map<String, Object> source = process(jsonFile);
  20. System.out.println(new Date().getTime());
  21. return new MapPropertySource("dbConfigMap",source);
  22. }
  23.  
  24. private Map<String, Object> process(final File resourceFile){
  25. Map<String, Object> map = null;
  26. try{
  27. map = new ObjectMapper().readValue(resourceFile, LinkedHashMap.class);
  28. }catch (IOException e){
  29. e.printStackTrace();
  30. }
  31. return map;
  32. }
  33. }

需要一个类拦截并获取Config Server在正常处理请求后返回的内容,以便接下来的改写:

  1. import javax.servlet.ServletOutputStream;
  2. import javax.servlet.http.HttpServletResponse;
  3. import javax.servlet.http.HttpServletResponseWrapper;
  4. import java.io.ByteArrayOutputStream;
  5. import java.io.IOException;
  6. import java.io.PrintWriter;
  7.  
  8. public class MyResponseWrapper extends HttpServletResponseWrapper {
  9. private ByteArrayOutputStream byteArrayOutputStream;
  10. private ServletOutputStream servletOutputStream;
  11. private PrintWriter printWriter;
  12.  
  13. public MyResponseWrapper(HttpServletResponse response) {
  14. super(response);
  15. byteArrayOutputStream = new ByteArrayOutputStream(4096);
  16. printWriter = new PrintWriter(byteArrayOutputStream);
  17. servletOutputStream = new MyServletOutputStream(byteArrayOutputStream);
  18. }
  19.  
  20. @Override
  21. public PrintWriter getWriter() throws IOException{
  22. return printWriter;
  23. }
  24.  
  25. @Override
  26. public ServletOutputStream getOutputStream() throws IOException{
  27. return servletOutputStream;
  28. }
  29.  
  30. @Override
  31. public void flushBuffer() throws IOException{
  32. if(printWriter!=null){
  33. printWriter.flush();
  34. }
  35. if(servletOutputStream !=null){
  36. servletOutputStream.flush();
  37. }
  38. }
  39.  
  40. @Override
  41. public void reset(){
  42. byteArrayOutputStream.reset();
  43. }
  44.  
  45. public byte[] getResult(){
  46. try{
  47. flushBuffer();
  48. }catch (IOException e){
  49.  
  50. }
  51. return byteArrayOutputStream.toByteArray();
  52. }
  53. }

对ServletOutputStream的封装类:

  1. import javax.servlet.ServletOutputStream;
  2. import javax.servlet.WriteListener;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5.  
  6. public class MyServletOutputStream extends ServletOutputStream {
  7. ByteArrayOutputStream output;
  8.  
  9. public MyServletOutputStream(ByteArrayOutputStream output) {
  10. this.output = output;
  11. }
  12.  
  13. @Override
  14. public void write(int b) throws IOException {
  15. output.write(b);
  16. }
  17.  
  18. public void write(byte[] data, int offset, int length) {
  19. output.write(data, offset, length);
  20. }
  21.  
  22. @Override
  23. public boolean isReady() {
  24. return false;
  25. }
  26.  
  27. @Override
  28. public void setWriteListener(WriteListener listener) {
  29.  
  30. }
  31. }

有了拦截与处理的方法,最后就是把功能组合到拦截器中:

  1. import com.alibaba.fastjson.JSON;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.fang.microservice.cloud.Loader.JsonSourcePropertiesLoader;
  5. import com.fang.microservice.cloud.Wrapper.MyResponseWrapper;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.boot.web.servlet.ServletComponentScan;
  8. import org.springframework.core.env.MapPropertySource;
  9. import org.springframework.stereotype.Component;
  10.  
  11. import javax.servlet.*;
  12. import javax.servlet.annotation.WebFilter;
  13. import javax.servlet.http.HttpServletResponse;
  14. import java.io.*;
  15.  
  16. @Component
  17. @ServletComponentScan
  18. @WebFilter(urlPatterns = "/{name}/{profiles:.*[^-].*}", filterName = "ConfigServletFilter")
  19. public class ConfigAspectFilter implements Filter {
  20. @Autowired
  21. JsonSourcePropertiesLoader jsonConfig;
  22.  
  23. @Override
  24. public void init(FilterConfig filterConfig) throws ServletException {
  25.  
  26. }
  27.  
  28. @Override
  29. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  30. MyResponseWrapper wrapper = new MyResponseWrapper((HttpServletResponse) response);
  31. chain.doFilter(request, wrapper);
  32. byte[] respBytes = wrapper.getResult();
  33. String result = Byte2String(respBytes);
  34. if (response.getContentType().toLowerCase().contains("application/json")) {
  35. if (!result.toLowerCase().contains("datasource.mysql.")) {
  36. JSONObject configObj = (JSONObject) JSON.parse(result);
  37. JSONArray propertySourcesObjs = configObj.getJSONArray("propertySources");
  38. if (propertySourcesObjs.size() > 0) {
  39. JSONObject propertySourcesObj = (JSONObject) propertySourcesObjs.get(0);
  40. JSONObject sourceObj = propertySourcesObj.getJSONObject("source");
  41. MapPropertySource mps = jsonConfig.load();
  42. for (String key : mps.getPropertyNames()) {
  43. sourceObj.put(key, mps.getProperty(key));
  44. }
  45. result = JSON.toJSONString(configObj);
  46. }
  47. }
  48. }
  49.  
  50. response.setContentLength(-1);
  51. PrintWriter out = response.getWriter();
  52. out.write(result);
  53. out.flush();
  54. out.close();
  55. }
  56.  
  57. public static String Byte2String(byte[] buff) {
  58. ByteArrayOutputStream result = new ByteArrayOutputStream();
  59. int length;
  60. try {
  61. result.write(buff, 0, buff.length);
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. }
  65. try {
  66. return result.toString("UTF-8");
  67. } catch (UnsupportedEncodingException e) {
  68. e.printStackTrace();
  69. }
  70. return "";
  71. }
  72.  
  73. @Override
  74. public void destroy() {
  75.  
  76. }
  77. }

作为敏感数据存储的文件内容:

  1. { "testDbName":"testDb","userName":"userName","password":"pass"}

测试效果如下:

最后不能忘记特定敏感文件的路径,需要配置在配置文件中:

Config.Json.FileLocation=/xx/test/test.json

这样,一个既可以规避敏感文件,又能不失Config Server方便性的简单处理方案就这样可以投入使用了。

  1.  

Spring Cloud 自定义ConfigServer的更多相关文章

  1. Spring Cloud 自定义ConfigServer 解决敏感信息存储问题

    公司需要将系统配置信息中的敏感信息独立存放. 现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求: (1)数据库配置信息分离(主要是Db信息). ...

  2. spring cloud 自定义ribbon客户端

    一.自定义Ribbon客户端-[方式一]配置类 1.1.自定义负载规则 增加RibbonConfiguration.java配置类 public class RibbonConfiguration { ...

  3. Spring Cloud教程合集

    Spring Cloud系列终于搞完啦! 这一系列是笔者的学习笔记,原书之前也给小伙伴们推荐过 <Spring Cloud微服务实战> 原书采用了较老的Brixton版,笔者在学习的过程中 ...

  4. spring cloud 2.x版本 Config配置中心教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前面的文章eureka-server的实现. 参考 eureka-server ...

  5. Spring Cloud 升级之路 - 2020.0.x - 7. 使用 Spring Cloud LoadBalancer (2)

    本项目代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们使用 Spri ...

  6. SpringCloud升级之路2020.0.x版-24.测试Spring Cloud LoadBalancer

    本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 通过单元测试,我们 ...

  7. spring cloud 服务A调用服务B自定义token消失,记录

    后端:spring cloud 前端:vue 场景:前端ajax请求,包装自定义请求头token到后台做验证,首先调用A服务,A服务通过Feign调用B服务发现自定义token没有传到B服务去; 原因 ...

  8. Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑

    应用场景 上一篇<Spring Cloud Stream消费失败后的处理策略(一):自动重试>介绍了默认就会生效的消息重试功能.对于一些因环境原因.网络抖动等不稳定因素引发的问题可以起到比 ...

  9. Spring Cloud(7):Zuul自定义过滤器和接口限流

    上文讲到了Zuul的基本使用: https://www.cnblogs.com/xuyiqing/p/10884860.html 自定义Zuul过滤器: package org.dreamtech.a ...

随机推荐

  1. Linux中常见问题(磁盘 定时任务)

    第1章 linux无法上网 1)     第一步,先ping域名. ping www.baidu.com 2)再ping一个公网ip , ping 223.5.5.5/223.6.6.6/114.11 ...

  2. Azkaban安装部署

    在root的用户下搭建的 • Azkaban安装部署(可参照:http://azkaban.github.io/azkaban/docs/latest/) 1):前提 安装JDK,安装Hadoop,H ...

  3. 深入分析Android动画(一)

    动画的分类: ①View动画 View动画顾名思义其作用对象为View,包含平移.缩放.旋转.透明,这四类变化分别对应着Animation的子类TranlateAnimation.ScaleAnima ...

  4. 外键删除(T-SQL Drop Foreign Key)

    列出某张表相关的 FK Name select distinct name from sys.objects where object_id in (   select fk.constraint_o ...

  5. 分享:苹果APP更新上架被拒的另一种理由(Safety - Objectionable Content)

    这两个星期,本来想和大伙分享:写IT连创业系列运营篇. 但时间飞过,仍只是写了开头,一直很忙,没能完往下写. 今天就动手写点其它内容,哈哈,免的和小伙伴太陌生〜〜〜 前几天更新了:IT恋和IT连的版本 ...

  6. python之optparse模块

    测试例子 #!/usr/bin/env python2.7 import sys import os from optparse import OptionParser def parse_optio ...

  7. MySQL长短密码

    MySQL长短密码 今天批量搭建MySQL环境的时候,遇到长短密码问题,故就此问题总结一下长短密码. 介绍 1.长密码例子: mysql> show grants for 'test'@'loc ...

  8. JSF页面中使用js函数回调后台action方法

    最近遇到了一个问题就是在JSF页面中嵌入html页面,这个html页面中很多功能是使用js动态生成的,现在需要在js函数里想去调用JSF中action类method()方法并动态传送数据给后台进行处理 ...

  9. STM32F030如何正确配置IO口的复用功能

    本文所使用的单片机型号为STM32F030C8T6. 在030系列的单片机中,PA2引脚除了作为普通的IO引脚用作输入输出功能以外,还可以作为内部外设串口1,串口2,定时器15通道1这三个外设的功能引 ...

  10. Redis 快速入门

    Redis 快速入门 谈到Redis,大家应该都不陌生.它是用c语言开发的一个高性能键值数据库,主要用于缓存领域.本章通过Redis的安装,Redis的五大数据类型,Redis的Java客户端,Red ...