Spring为扩展配置服务,提供了一个基于HTTP的资源绑定的api。(键值对,或者YAML内容) 此服务能够经过
@EnableConfigServer
很是容易的整合进Spring Boot应用中。java
例如:ConfigServer.java
mysql
@SpringBootApplication @EnableConfigServer public class ConfigServer { public static void main(String[] args) { SpringApplication.run(ConfigServer.class, args); } }
默认状况下,和Spring Boot应用同样运行在8080端口,固然也有不少种方法修改此端口。最简单的方法,设置默认的配置资源库。 经过在jar中
configserver.yml
文件中配置spring.config.name=configserver
;或者经过指定本身的configserver.yml
。git
例如:application.properties
github
server.port: 8888 spring.cloud.config.server.git.uri: file://${user.home}/config-repo
${user.home}/config-repo
,是一个git资源。内容为YAML或者properties文件内容。正则表达式
例如:算法
$ cd $HOME $ mkdir config-repo $ cd config-repo $ git init . $ echo info.foo: bar > application.properties $ git add -A . $ git commit -m "Add application.properties"
注意: 以上使用本地文件只是为了测试。生产中应该使用远程服务提供的配置资源。spring
在Config Server中,对于配置数据你想要怎么样存储呢?存储策略由
Environment
中的EnvironmentRepository
来决定。同时多个Environment
之间也容许复制配置项,甚至配置源propertySources
。sql
Environment
资源主要来自这三个参数:bootstrap
{application}
对应着客户端的spring.application.name配置;{profile}
对应着客户端spring.profiles.active配置;{label}
这是一个服务端功能,标记着配置文件的版本。
资源实例一般表现和Srping Boot应用加载配置文件相似:
spring.config.name
相似于{application}
参数;spring.profiles.active
相似于{profiles}
参数。 对于特定配置文件的优先级也是相似Spring Boot的规则:特定的配置会覆盖默认配置,而且若是指定了多个,那最后一个优先级最高。api
例如:一个客户端应用能够配置一下引导配置:
bootstrap.yml
spring: application: name: foo profiles: active: dev,mysql
普通Spring Boot应用也能够经过环境变量或者命令行方式配置以上信息
若是资源是基于文件方式的,那系统会从
application.yml
建立一个Environment
共享给全部客户端,而且加载foo.yml
进行覆盖。 若是YAML文件中有指定特定配置的话,那么这些特定配置比默认配置拥有着更高的优先级。 这些拥有更高优先级的配置文件在Environment
以前,就逐个生成PropertySource
并被加载。
默认状况下
EnvironmentRepository
是基于GIT的,这样很是方便管理更新、物理环境也方便对修改历史进行审计。 能够经过spring.cloud.config.server.git.uri
配置属性修改Config Server的本地资源库(例如:在application.yml
中修改)。 若是设置成file:前缀,那会从本地资源进行加载,这样方便你快速简易的启动服务。可是这样就是直接在本地资源库上进行操做,没有备份。(不过若是远程服务资源是不变得状况下,这种方式也无所谓。)
若是须要扩展Config Server使其具有高可用性,那你须要将全部的服务实例指向同一个资源,这样就工做在一个共享文件的环境下。在这个状况下最好使用ssh:协议去访问共享文件资源,这样能够备份资源同时可使用本地缓存提升效率。
资源实例会把HTTP资源中的
{label}
映射成git标签(commit id, branch name 或者 tag) 若是git分支或者标签名上有斜杠"/"那映射成HTTP URL时会自动替换成"_"。使用括号、空格时须要当心。(例如使用cmd时须要加上双引号)
当须要时,Spring Cloud Config Server 支持在GIT URI中使用
{application}
、{profile}
、{label}
三个占位符,可是要记住{label}
老是会映射成git的标签。
spring: cloud: config: server: git: uri: https://github.com/myorg/{application}
在应用配置文件与特定配置文件中能够同过正则表达式来支持更为复杂的状况。能够在
{application}/{profile}
中可使用通配符进行匹配,若是有多个值可使用逗号分隔。
spring: cloud: config: server: git: uri: https://github.com/spring-cloud-samples/config-repo repos: simple: https://github.com/simple/config-repo special: pattern: special*/dev*,*special*/dev* uri: https://github.com/special/config-repo local: pattern: local* uri: file:/home/configsvc/config-repo
若是
{application}/{profile}
没有匹配到任何资源,则使用spring.cloud.config.server.git.uri
配置的默认URI。
上面例子中
pattern
属性是一个YAML数组,也可使用YAML数组格式来定义。这样能够设置成多个配个配置文件。
spring: cloud: config: server: git: uri: https://github.com/spring-cloud-samples/config-repo repos: development: pattern: - */development - */staging uri: https://github.com/development/config-repo staging: pattern: - */qa - */production uri: https://github.com/staging/config-repo
每一个资源库有一个可选的配置,用来指定扫描路径。
spring: cloud: config: server: git: uri: https://github.com/spring-cloud-samples/config-repo searchPaths: foo,bar*
这样系统就会自动搜索foo的子目录,以及以bar开头的文件夹中的子目录。
默认状况下,当第一次请求配置时,系统复制远程资源库。系统也能够配置成一启动就复制远程资源库。
spring: cloud: config: server: git: uri: https://git/common/config-repo.git repos: team-a: pattern: team-a-* cloneOnStart: true uri: http://git/team-a/config-repo.git team-b: pattern: team-b-* cloneOnStart: false uri: http://git/team-b/config-repo.git team-c: pattern: team-c-* uri: http://git/team-a/config-repo.git
上面的例子中team-a的资源库会在启动时就从远程资源库进行复制,其余的则等到第一次请求时才从远程资源库复制。
若是远程资源库设置了权限认证,则能够以下配置:
spring: cloud: config: server: git: uri: https://github.com/spring-cloud-samples/config-repo username: trolley password: strongpassword
若是你不是用HTTPS和用户认证,可使用SSH uri 的格式。例如:
git@github.com:configuration/cloud-configuration
这样你就须要先有SSH的key。 这种方式系统会使用JGit
库进行访问,能够去查看相关文档。能够在~/.git/config
中设置HTTPS代理配置,或者也能够经过JVM参数-Dhttps.proxyHost
、-Dhttps.proxyPort
来配置代理。
提示: 当你不知道你的
~/.git
目录时,可使用git config --global
来指定。例如:git config --global http.sslVerify false
Spring Cloud 的 Config Server 也支持在搜索路径中使用
{application}
、{profile}
、{label}
占位符配置。例如:
spring: cloud: config: server: git: uri: https://github.com/spring-cloud-samples/config-repo searchPaths: '{application}'
也有不使用git资源库的方式,那就是从本地classpath或者文件系统加载配置文件。这种方式能够经过
spring.profiles.active=native
开启。
使用file:前缀加载文件系统,不然从classpath中加载,也可使用
${}
样式的环境占位符。例如:file:///${user.home}/config-repo
searchLocations
默认值与本地Spring Boot应用的扫描路径同样,都是:[classpath:/, classpath:/config, file:./, file:./config]
这样并不用担忧application.properties
文件暴露给全部客户端,由于在发送给客户端以前就会被清理掉。
基于文件系统的配置服务对于测试和快速练手来讲是比较好的,若是用于生产环境,那就须要确保文件系统的可靠性,并容许共享访问。
路径搜索时可以包含
{application}
、{profile}
、{label}
,这样方便你按照目录来管理你的配置文件。
若是在路径搜索时不使用占位符,那也会尝试自动的在HTTP资源中加上
{label}
后缀,那这样就会从不一样的路径加载到。所以,在默认状况下不使用占位符等价于在每个路径后添加了/{label}/
。例如:file:/tmp/config
就等价于file:/tmp/config,file:/tmp/config/{label}
经过基于文件(svn,本地)资源,文件名为
application*
的资源将在全部的应用客户端中共享。(application.properties
,application.yml
,application-*.properties
) 能够经过这种方式来定义一个全局的默认配置,若有必要应用可使用应用指定配置对其进行覆盖。
Config Server 有一个属性覆盖的特性,容许操做者经过提供一个配置属性去覆盖全部应用中的配置。经过普通的Spring Boot钩子方式来实现,所以应用不须要什么改变。
声明覆盖仅仅须要在
spring.cloud.config.server.overrides
中配置一个键值对。例如:
spring: cloud: config: server: overrides: foo: bar
这样会引发全部的应用客户端去读取
foo=bar
去覆盖本身的配置。(固然应用拿到新的数据后本身决定如何使用,所以,覆盖并非强制的,客户端能够自定义拿到新数据后的行为)
提示: 使用文件方式时,Spring环境中的占位符
${}
能够用""对"$"转义,例如:\${app.foo:bar}
。当使用YAML时,YAML自己会处理,所以不须要转义。
Config Server 经过一个健康指示器来检测配置的
EnvironmentRepository
是否正常工做。 默认状况下会向EnvironmentRepository
询问一个名字为app
的应用配置,EnvironmentRepository
实例回应default
配置。
能够经过配置让健康指示器一块儿去检查多个应用的多个配置。例如:
spring: cloud: config: server: health: repositories: myservice: label: mylabel myservice-dev: name: myservice profiles: development
也能够经过配置
spring.cloud.config.server.health.enabled=false
去关闭此功能。
你能够按你本身的状况用任何方法对Config Server进行安全处理。(从物理网络安全到OAuth2受权token),不过经过Spring Security 结合Spring Boot能提供一种更好的方式。
使用Spring Boot默认的基于HTTP安全方式,仅仅须要引入Spring Security依赖。(如:能够经过
spring-boot-starter-security
)
默认状况使用一个用户名和一个随机产生的密码,这种方式并非很靠谱,所以,建议经过
spring-boot-starter-security
配置密码,并对其进行加密处理。
重要:要使用此特性,须要彻底的JCE受权,方法参见前文
若是远程资源是一个通过加密的内容(以
{cipher}
开头),在发送给客户端以前会被解密。 这样,配置内容就不用明文存放了。 当直接去替换一个没有解密的值时,会被标记为"invalid"(无效的)。 这基本上能够大部分的杜绝密钥泄露的发生。例如:
application.yml
spring: datasource: username: dbuser password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'
若是使用配置文件则加密数据不要加上双引号。例如:
application.properties
spring.datasource.username: dbuser spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
这样就能够安全共享此文件,同时能够保护密钥。
这个服务经过
/encrypt
和/decrypt
端点向外暴露。这样就能够用过POST方式向/encrypt
提交加密后的数据。例如:
$ curl localhost:8888/encrypt -d mysecret 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
反过来也行,经过
/decrypt
安全提交数据。(前提是已经在服务端配置了相应的解密KEY)
$ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
在提交以前,存放这些加密数据存在着潜在的不安全性。
/encrypt
和/decrypt
都接受一个路径/*/{name}/{profiles}
用于分开控制每个应用的密码。
注意: 若是须要为每个应用使用不一样的密码,则须要一个
@Bean
产生一个TextEncryptorLocator
对象来建立不一样的密钥对,并给它们赋予一个名字。固然这是可选的,默认不须要这样(全部应用使用相同的密钥)
Spring 命令行客户端(Spring Cloud CLI)也可使用加解密特性。例如:
$ spring encrypt mysecret --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda $ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda mysecret
可使用
@
来指定一个路径,包含一个存放加解密key 的文件。例如:
$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
Config Server 可使用对称/非对称加解密算法。使用非对称算法拥有更好的安全性,可是对称算法更方便。
配置对称算法的key,只须要设置
encrypt.key
就好了。(或者使用环境变量ENCRYPT_KEY
)
配置非对称算法key,你能够选择在
encrypt.key
中配置一个PEM编码的文本,也能够经过encrypt.keyStore.*
配置使用一个密钥库。
encrypt.keyStore.*
包括以下配置:
- location 一个资源路径
- password 密钥库密码
- alias 被使用的密钥标识
经过公钥加密,私钥解密。所以,原则上能够在服务端只配置公钥。可是实践中可能不多这样作,密钥管理在所有客户端处理过程都会被包含,而不只仅是服务端。不过从另外一方面说,若是服务端 真的不安全,并且只有少数几个客户端须要加密处理,那这样配置也有必定的合理性。
能够经过以下配置来建立一个用于测试的密钥库:
$ keytool -genkeypair -alias mytestkey -keyalg RSA \ -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \ -keypass changeme -keystore server.jks -storepass letmein
把
server.jks
文件放入classspath,而后在application.yml
中进行如下配置:
encrypt: keyStore: location: classpath:/server.jks password: letmein alias: mytestkey secret: changeme
经过添加
{cipher}
前缀来代表使用加密数据,系统会在对密文进行Base64解码以前寻找{name:value}
前缀信息。密钥经过TextEncryptorLocator
(不管哪一种)实例,最终使用TextEncryptor
来完成加解密。 若是配置了密钥库(encrypt.keystore.location
),那默认的执行器(locator)就会按照配置的alias去密钥库中查找相应的密钥。例如:
foo: bar: `{cipher}{key:testkey}...`
上例中执行器(locator)将会去查找一个叫作“testkey”的密钥。密钥库的密码能够经过
{secret:…}
来指定,可是如非必要通常不指定。若是想使用密钥库密码,那建议使用定制SecretLocator
对其加密处理。
若是只是对不多的配置数据进行加密的话,密钥轮换基本上没有必要。 可是,偶还仍是会有需求去修改密钥的场景。在这种状况下,全部的客户端都须要改变源配置文件(如:git)来使用新的
{key:…}
,最好还要事先检查密钥库中的密钥。
提示:
{name:value}
也能够在/encrypt
数据请求时使用。
有的时候须要客户端对配置数进行解密,而不是在服务端解密。这种状况下,仍然能够经过
/encrypt
和/decrypt
端点访问。那就须要明确指定配置数据在服务端发出时不解密:spring.cloud.config.server.encrypt.enabled=false
。若是不关系端点访问,那就既不要配置密钥也不要开启此配置。