https://github.com/carnellj/spmia-chapter3
基于云的微服务开发强调以下几点:
应用程序的配置与正在部署的实际代码完全分离
构建服务器、应用程序以及一个不可变的镜像,他们在各环境中进行提升时永远不会发生变化
在服务器启动时通过环境变量注入应用程序配置信息,或者在微服务启动时通过集中式存储库读取应用程序的配置信息
管理配置
应用程序配置管理要遵循的4条原则:
分离:将服务配置信息与服务的实际物理部署完全分开。应用程序配置不应与服务实例一起部署。相反,配置信息应该作为环境变量传递给正在启动的服务,或者在服务启动时从集中式存储库中读取
抽象:将访问配置数据的功能抽象到一个服务接口中。应用程序使用基于REST的JSON服务来检索配置数据,而不是编写直接访问服务存储库的代码(也就是从文件或使用JDBC从数据库读取数据)
集中:因为基于云的应用程序可能会有数百个服务,所以最小化用于保存配置信息的不同存储库的数量至关重要。将应用程序配置集中在尽可能少的存储库中
稳定:因为应用程序的配置信息与部署的服务完全隔离并集中存放,所以不管采用何种方案实现,至关重要的一点就是保证其高可用和冗余
要记住一个关键点:将配置信息与实际代码分开之后,开发人员将创建一个需要进行管理和版本控制的外部依赖项
Spring Cloud配置服务器
1、依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>应用程序配置文件的命名约定是“应用程序名称-环境名称.yml”。环境名称直接转换为可以浏览配置信息的URL。要运行哪个服务环境是由在命令行服务启动时传入的Spring Boot的profile指定的。
注意:建议不要在中大型云应用中使用基于文件系统的解决方案。使用文件系统方法,意味着要为想要访问应用程序配置数据的所有云配置服务器实现共享文件挂载点。
2、使用带有文件系统的Spring Cloud配置服务器
server: port: 8888 spring: profiles: #native表示使用文件系统配置 active: native cloud: server: native: #searchLocations属性为每一个应用程序提供了用逗号分隔的文件夹列表,这些文件夹含有由配置服务器管理的属性 searchLocations: file:///Users/johncarnellj/book/spmia-code/chapter3-code/confsvr/src/main/resources/config/licensingservice 访问:http://localhost:8888/licensingservice/dev在访问开发环境端点时,将返回许可证服务的默认配置属性以及开发环境下的许可证配置。
Spring Cloud配置返回两组配置信息的原因是,Spring框架实现了一种用于解析属性的层次结构机制。当Spring框架执行属性解析时,它将始终先查找默认属性中的属性,然后用特定环境的值去覆盖默认属性。
具体来说,如果在licensingservice.yml文件中定义一个属性,并且不在任何其他环境配置文件(如licensingservice-dev.yml)中定义它,则Spring框架将使用这个默认值。
3、使用Spring Cloud配置服务器和Git(与步骤2区分)
server: port: 8888 spring: cloud: server: #告诉Spring Cloud Config使用Git作为后端存储库 git: #告诉Spring Cloug Git服务器和Git存储库的URL uri: https://github.com/carnellj/config-repo/ #告诉Spring Cloud Config在Git中查找配置文件的路径 searchPaths: licensingservice, organizationservice username: native-cloud-apps passowrd: 0ffended4、使用Spring Cloud配置服务器刷新属性
@SpringBootApplication @EnableConfigServer // 定义该服务为配置服务器 // 允许访问/refresh端点刷新 // 注解只会重新加载应用程序配置中的自定义Spring属性。Spring Data使用的数据库配置等不会被@RefreshScope注解重新加载。 // 要执行刷新,访问http://<yourserver>:8080/refresh @RefreshScope public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }Spring Cloud Config与Spring Boot客户端集成
1、依赖
<!-- 数据库配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifaceId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>91-901.jdbc4</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>2、配置使用Spring Cloud Config
spring: application: #指定许可证服务的名称,以便Spring Cloud Config客户端知道正在查找哪个服务 name: licensingservice profiles: #指定服务应该运行的默认profile映射到环境 active: default cloud: config: #指定Spring Cloud Config服务器的位置 uri: http://localhost:8888 运行:mvn spring-boot: run 通过运行此命令而不设置任何属性,许可证服务器将自动尝试使用端点http://localhost:8888和在许可证服务的bootstrap.yml文件中定义的活跃profile,连接到Spring Cloud Config配置服务器。 如果要覆盖这些默认值并指向另一个环境,可以通过将许可证服务项目编译到jar,然后使用-D系统属性来运行这个jar来实现: java -Dspring.cloud.config.uri=http://localhost:8888 \ -Dspring.profiles.active=dev \ -jar target/licensing-service-0.0.1-SNAPSHOT.jar可以通过访问http://localhost:8888/env来确认正在运行的环境:
3、使用Spring Cloud配置服务器连接数据源
@Entity @Table(name = "licenses") public class License { @Id @Column(name = "license_id", nullable = false) private String licenseId; @Column(name = "organization_id", nullable = false) private String organizationId; @Column(name = "product_name", nullable = false) private String productName; ..... } @Repository public interface LicenseRepository extends CrudRepository<License, String> { public List<License> findByOrganizationId(String organizationId); public License findByOrganizationIdAndLicenseId(String organizationId, String licenseId); } @Component public class ServiceConfig { @Value("${example.property}") private String exampleProperty; public String getExampleProperty() { return exampleProperty; } } @Service public class LicenseService { @Autowired private LicenseRepository licenseRepository; @Autowired ServiceConfig config; public License getLicense(String organizationId, String licenseId) { License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId); return license.withComment(config.getExampleProperty()); } public List<License> getLicensesByOrg(String organizationId) { return licenseRepository.findByOrganizationId(organizationId); } public void saveLicense(License license) { license.withId(UUID.randomUUID().toString()); licenseRepository.save(license); } }保护敏感的配置信息
Spring Cloud Config可以让我们轻松加密敏感属性。
下载并安装加密所需的Oracle JCE jar
创建加密密钥
加密和解密属性
配置微服务以在客户端使用加密
1、下载并安装加密所需的Oracle JCE jar
它无法通过maven下载,必须从Oracle下载,下载包含JCE jar的zip文件后,必须执行以下操作:
切换到$JAVA_HOME/jre/lib/security文件夹
将$JAVA_HOME/jre/lib/security目录中的local_policy.jar和US_export_policy.jar文件备份到其他位置
解压从Oracle下载的JCE zip文件
将local_policy.jar和US_export_policy.jar复制到$JAVA_HOME/jre/lib/security目录中
配置Spring Cloud Config以使用加密
2、创建加密密钥
一旦jar文件就位,就需要设置一个对称加密密钥。
对称加密密钥只不过是加密器用来加密值解密器用来解密的共享密钥。使用Spring Cloud配置服务器,对称加密密钥是通过操作系统环境变量ENCRYPT_KEY传递给服务的字符串。
export ENCRYPT_KEY=IMSYMMETRIC关于对称加密,要注意以下两点:
对称密钥的长度应该是12个或更多个字符,最好是一个随机的字符集
不要丢失对称密钥。一旦使用加密密钥加密某些东西,如果没有对称密钥就无法解密
3、加密和解密属性
在启动Spring Cloud Config实例时,Spring Cloud Config将检测到环境变量ENCRYPT_KEY已设置,并自动将两个新端点(/encrypt和/decrypt)添加到Spring Cloud Config服务。使用/encrypt端点加密p0stgr@s,使用/decrypt端点解密。
将已加密的属性添加到Github或基于文件系统的许可证服务的配置文件中:
// Spring Cloud配置服务器要求所有已加密的属性前面加上{cipher},{cipher}告诉Spring Cloud配置服务器它正在处理已加密的值 spring.datasource.password: "{cipher}xxxxxxxx"启动Spring Cloud配置服务器,并使用GET方法访问http://localhost:8888/licensingservice/default端点。但仍然存在一个问题,在访问http://localhost:8888/licensingservice/default端点时,数据库密码被以纯文本形式公开了。
在默认情况下,Spring Cloud Config将在服务器上解密所有属性,并将未加密的纯文本作为结果传回给请求属性的应用程序。但是,开发人员可以告诉Spring Cloud Config不要在服务器上进行解密,并让应用程序负责检索配置数据以解密已加密的属性。
4、配置微服务以在客户端使用加密
要让客户端对属性进行解密,需要做以下3件事情:
配置Spring Cloud Config不要在服务器端解密属性
在许可证服务器上设置对称密钥
将spring-security-rsa JAR添加到许可证服务的pom.xml文件中
在Spring Cloud Config中禁用服务器端属性的加密,可以通过设置Spring Cloud Config的application.yml文件中的spring.cloud.config.server.encrypt.enable属性为false来完成。
因为许可证服务现在负责解密已加密的属性,所以需要先在许可证服务上设置对称密钥,方法是确保ENCRYPT_KEY环境变量与Spring Cloud Config服务器使用的对称密钥相同(如IMSYMMETRIC)。
接下在,在许可证服务中包含spring-security-rsa JAR依赖:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-rsa</artifactId> </dependency>通过以上操作就能得到spring.datasource.password是以加密形式返回: