您的位置:首页 > 其它

如何保护Service-to-Service的微服务

2019-08-01 17:35 323 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/Developlee/article/details/100691928

点击左上角蓝字,关注“锅外的大佬”

专注分享国外最新技术内容

已构建的微服务架构,如何保证了服务与服务之间(service-to-service)通信的安全性?

你可以通过不在

docker-compose.yml
文件中暴露端口,使得服务之间的通信更加安全。但是,如果微服务应用程序的端口意外暴露又会发生什么呢?任何人都可以访问这些数据吗?

未雨绸缪,我将告诉你,如何使用HTTPS和OAuth 2.0保护服务与服务通信。

1.搭建微服务

我将使用Spring Boot,Spring Cloud和Spring Cloud Config快速构建完整的微服务架构。我朋友 Raphael 撰写了一篇关于 如何构建Spring微服务并将它们Docker化(Dockerize)用于生产 的文章。您可以使用他的示例应用程序作为起点。克隆  okta-spring-microservices-docker-example  项目:

git clone https://github.com/oktadeveloper/okta-spring-microservices-docker-example.git spring-microservices-security
cd spring-microservices-security
该项目需要Okta上的两个OpenID连接应用程序,一个用于开发(开发App),一个用于生产(生产App)。如果您没有完成上述教程,则需要在Okta上创建每个应用程序。

2. 在Okta上创建OpenID Connect应用程序

您可以注册一个 免费的开发者帐户  ,该帐户可以免费拥有1000个月活跃用户。对此例来说,应该已经足够了。

为什么使用Okta?因为编写身份验证并不有趣。Okta具有身份验证和用户管理API,可让您更快地开发应用程序。Okta的API和SDK使您可以在几分钟内轻松地对用户进行身份验证,管理和保护。

创建帐户后,在Okta的仪表板(应用程序  >  添加应用程序)中创建一个新的Web应用程序。为应用程序指定名称,复制现有的登录重定向URI,并使其使用HTTPS。单击  “完成”

结果应类似于下面的屏幕截图。

为生产App创建另一个应用。我将其命名为:

Prod Microservices

在克隆的项目中,修改config / school-ui.properties,获取开发App的设置。

okta.oauth2.issuer=https://okta.okta.com/oauth2/default
okta.oauth2.clientId={devClientId}
okta.oauth2.clientSecret={devClientId}
用 Maven 单独运行应用程序时将使用这些设置。使用
Docker
容器运行时将使用生产环境配置。修改  
config-data/school-ui-production.properties
以从生产App中获取设置。
okta.oauth2.clientId={prodClientId}
okta.oauth2.clientSecret={prodClientId}
docker-compose.yml
 使用
spring.profiles.active
 启用生产(prodcution)配置文件 :
school-ui:
image: developer.okta.com/microservice-docker-school-ui:0.0.1-SNAPSHOT
environment:
- JAVA_OPTS=
-DEUREKA_SERVER=http://discovery:8761/eureka
-Dspring.profiles.active=production
restart: on-failure
depends_on:
- discovery
- config
ports:
- 8080:8080
Docker Compose从应用程序上方的目录运行,并从
config-data
目录中读取其数据  。因此,您需要将这些属性文件复制到此目录中。在该项目的根目录运行以下命令。
cp config/*.properties config-data/.

3.使用Docker Compose启动微服务

该项目在其根目录下有一个聚合
pom.xml
  ,允许您使用一个命令构建所有项目。运行以下 Maven 命令为每个项目构建,测试以及构建Docker镜像。
mvn clean install
如果您没有安装 Maven ,可以使用SDKMAN安装它:
sdk install maven

完成此过程后,使用Docker Compose启动所有应用程序{config,discovery,school-service和school-ui}。如果没有安装Docker Compose,请参阅  安装Docker Compose。

docker-compose up -d
您可以使用  Kitematic  在启动时查看每个应用的日志。

浏览器访问:

http://localhost:8080
。在这样做之后,您应该能够登录并查看学校课程列表.

4.Spring Security和OAuth 2.0

这个例子使用了  Okta的Spring Boot Starter,它是Spring Security上的一个轻量级层。Okta启动器简化了配置并在访问令牌中进行了访问验证。它还允许您指定将用于创建Spring Security权限的声明。

docker-compose.yml
文件不会暴露  
school-service
给外界,通过不指定  
ports
来做到这一点。

school-ui
项目中的
SchoolController
类告知与
school-service
使用Spring的
RestTemplate
@GetMapping("/classes")
@PreAuthorize("hasAuthority('SCOPE_profile')")
public ResponseEntity<List<TeachingClassDto>> listClasses() {
return restTemplate
.exchange("http://school-service/class", HttpMethod.GET, null,
new ParameterizedTypeReference<List<TeachingClassDto>>() {});
}
您会注意到此类的端点上存 3ff7 在安全性,但服务之间不存在安全性。我将在下面的步骤中向您展示如何解决这个问题。

首先,暴露  

school-service
 的端口以模拟某人错误植入配置。更改  
school-service
docker-compose.yml
配置使其暴露端口。
school-service:
image: developer.okta.com/microservice-docker-school-service:0.0.1-SNAPSHOT
environment:
- JAVA_OPTS=
-DEUREKA_SERVER=http://discovery:8761/eureka
depends_on:
- discovery
- config
ports:
- 8081:8081
使用Docker Compose重启所有内容:
docker-compose down
docker-compose up -d
访问:
http://localhost:8081
, 你将无需进行身份验证即可查看数据 !可怕!

在继续下一部分之前,请确保关闭所有Docker容器。

docker-compose down

5.无处不在的HTTPS!

HTTPS代表 “安全” HTTP。HTTPS连接是加密的,其内容比HTTP连接更难以阅读。近年来,即使在开发过程中,也无处不在地使用HTTPS。使用HTTPS运行时可能会遇到一些问题,最好及早发现它们。

Let's Encrypt  是一个提供免费HTTPS证书的证书颁发机构。它还具有自动续订的API。简而言之,它使HTTPS变得如此简单,没有理由不使用它!有关 如何使用 Let's Encrypt生成证书的说明,  请参阅  添加社交登录到您的JHipster应用程序,以获取有关如何使用

certbot
Let's Encrypt 生成证书的说明。

我也鼓励您查看  Spring Boot Starter ACME。这是一个Spring Boot模块,可以使用Let’s Encrypt 和自动证书管理环境(ACME)协议简化生成证书。

6.使用mkcert轻松实现本地TLS

我最近发现了一个名为mkcert的工具   ,它允许创建  

localhost
证书。您可以在macOS上使用Homebrew安装它:
brew install mkcert
brew install nss # Needed for Firefox
如果您使用的是Linux,则需要先安装  
certutil
sudo apt install libnss3-tools
然后使用Linuxbrew 执行
brew install mkcert
。Windows用户可以 使用Chocolately或Scoop。

执行以下  

mkcert
命令为
localhost
生成一个证,  
127.0.0.1
您的计算机的名称,以及
discovery
主机(如在  
docker-compose.yml
中引用)。
mkcert -install
mkcert localhost 127.0.0.1 ::1 `hostname` discovery
如果生成带有数字的文件,则重命名文件,使其没有数字。
mv localhost+2.pem localhost.pem
mv localhost+2-key.pem localhost-key.pem

7.Spring Boot HTTPS

Spring Boot不支持带有 PEM  扩展的证书  ,但您可以将其转换为Spring Boot支持的
PKCS12
。您可以使用OpenSSL将证书和私钥转换为 PKCS12 。这也是Let's Encrypt生成的证书所必需的。

运行  

openssl
以转换证书:
openssl pkcs12 -export -in localhost.pem -inkey \
localhost-key.pem -out keystore.p12 -name bootifulsecurity
出现提示时指定密码。

在项目的根目录下创建  https.env  文件,并指定以下属性以启用HTTPS。

export  SERVER_SSL_ENABLED = true
export  SERVER_SSL_KEY_STORE = ../keystore.p12
export  SERVER_SSL_KEY_STORE_PASSWORD = {yourPassword}
export  SERVER_SSL_KEY_ALIAS = bootifulsecurity
export  SERVER_SSL_KEY_STORE_TYPE = PKCS12
更新  
.gitignore
文件,删除
.env
文件,使密钥库密码不会在源代码管理中终止。
*.env
运行  
source https.env
设置环境变量。或者,更好的是,将它添加到您的
.bashrc
或  
.zshrc
文件中,以便为每个新shell设置这些变量。当然,您也可以将它们包含在每个应用程序的  
application.properties
中。但之后您将密钥存储在源代码管理中。如果您没有将此示例检查到源代码管理中,则可以使用以下设置进行复制/粘贴。
server.ssl.enabled=true
server.ssl.key-store=../keystore.p12
server.ssl.key-store-password: {yourPassword}
server.ssl.key-store-type: PKCS12
server.ssl.key-alias: bootifulsecurity
启动  
discovery
应用:
cd discovery
source ../https.env
mvn spring-boot:run
然后确认您可以访问: 
https://localhost:8761

打开  

docker-compose.yml
并将所有实例的
http
更改为  
https
。编辑  
school-ui/src/main/java/ui/controller/SchoolController.java
将  
school-service
的调用改为使用HTTPS。
return restTemplate
.exchange("https://school-service/class", HttpMethod.GET, null,
new ParameterizedTypeReference<List<TeachingClassDto>>() {});
更新  
{config,school-service,school-ui}/src/main/resources/application.properties
中使每个实例注册为安全的应用程序。
eureka.instance.secure-port-enabled=true
eureka.instance.secure-port=${server.port}
eureka.instance.status-page-url=https://${eureka.hostname}:${server.port}/actuator/info
eureka.instance.health-check-url=https://${eureka.hostname}:${server.port}/actuator/health
eureka.instance.home-page-url=https://${eureka.hostname}${server.port}/
此外,更改每个
application.properties
(和  
bootstrap.yml
)中的Eureka地址为:
https://localhost:8761/eureka

school-ui
工程中的端口号尚未指定,因此需要添加:
server.port=8080
.

此时,您应该能够通过在每个项目中运行以下内容(在单独的终端窗口中)来启动所有应用程序。

source ../https.env
./mvnw spring-boot:start
确认一切正常  
https://localhost:8080
。然后杀掉所有进程: 
killall java

8.在Docker Compose中使用HTTPS

Docker不会读取环境变量,它不清楚本地CA(证书颁发机构),也无法将父目录中的文件添加到映像。

要解决此问题,需要复制  

keystore.p12
和  
localhost.pem
到每个项目的目录。第一个将用于Spring Boot,第二个将添加到每个映像的Java Keystore。
cp localhost.pem keystore.p12 config/.
cp localhost.pem keystore.p12 discovery/.
cp localhost.pem keystore.p12 school-service/.
cp localhost.pem keystore.p12 school-ui/.
然后修改每个项目  
Dockerfile
,来复制证书并将其添加到其信任库。
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/*.jar app.jar
ADD keystore.p12 keystore.p12
USER root
COPY localhost.pem $JAVA_HOME/jre/lib/security
RUN \
cd $JAVA_HOME/jre/lib/security \
&& keytool -keystore cacerts -storepass changeit -noprompt \
-trustcacerts -importcert -alias bootifulsecurity -file localhost.pem
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
然后Spring Boot和HTTPS的环境变量创建
.env
文件。
SERVER_SSL_ENABLED=true
SERVER_SSL_KEY_STORE=keystore.p12
SERVER_SSL_KEY_STORE_PASSWORD={yourPassword}
SERVER_SSL_KEY_ALIAS=bootifulsecurity
SERVER_SSL_KEY_STORE_TYPE=PKCS12
EUREKA_INSTANCE_HOSTNAME={yourHostname}
您可以
{yourHostname}
通过运行中的
hostname

Docker Compose有一个“env_file”配置选项,允许您读取此文件以获取环境变量。更新  

docker-compose.yml
为每个应用程序指定  
env_file
version: '3'
services:
discovery:
env_file:
- .env
...
config:
env_file:
- .env
...
school-service:
env_file:
- .env
...
school-ui:
env_file:
- .env
...
可以通过在根目录运行:
docker-compose config
来确保它正常工作  。

运行  

mvn clean install
重新构建所有Docker镜像,并启用HTTPS以进行Eureka注册:
docker-compose up -d
现在,所有应用都使用HTTPS在Docker中运行!可访问  
https://localhost:8080
来验证。

If your apps do not start up or can’t talk to each other, make sure your hostname matches what you have in

.env
.

还可以进一步提高安全性:使用OAuth 2.0来保护

school-service
API。

9.OAuth 2.0的API安全性

将Okta Spring Boot Starter和Spring Cloud Config添加到  

school-service/pom.xml
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
然后在
school-service/src/main/java/…/service/configuration
创建一个  
SecurityConfiguration.java
package com.okta.developer.docker_microservices.service.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
创建  
school-service/src/test/resources/test.properties
文件并添加属性,使Okta的配置通过,并且在测试时不使用服务发现或配置中心服务。
okta.oauth2.issuer=https://okta.okta.com/oauth2/default
okta.oauth2.clientId=TEST
spring.cloud.discovery.enabled=false
spring.cloud.config.discovery.enabled=false
spring.cloud.config.enabled=false
然后修改
ServiceApplicationTests.java
加载
test properties
:
import org.springframework.test.context.TestPropertySource;
...
@TestPropertySource(locations="classpath:test.properties")
public class ServiceApplicationTests {
...
}
添加
school-service/src/main/resources/bootstrap.yml
允许此实例从Spring Cloud Config读取其配置。
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:https://localhost:8761/eureka}
spring:
application:
name: school-service
cloud:
config:
discovery:
enabled: true
serviceId: CONFIGSERVER
failFast: true
然后复制 将
config/school-ui.properties
等价复制到
school-service
cp config/school-ui.properties config/school-service.properties
对于Docker Compose,您还需要
config-data/school-service.properties
,并添加以下配置:
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId={prodClientId}
okta.oauth2.clientSecret={prodClientId}
您还需要修改  
docker-compose.yml
以便  
school-service
在失败时重新启动。
school-service:
...
restart: on-failure
需要做的最后一步是修改  
SchoolController
(在  
school-ui
项目中),以向其发出的请求添加OAuth 2.0访问令牌。

示例1.将AccessToken添加到RestTemplate

package com.okta.developer.docker_microservices.ui.controller;
import com.okta.developer.docker_microservices.ui.dto.TeachingClassDto;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.stere
8000
otype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.util.List;
@Controller
@RequestMapping("/")
public class SchoolController {
private final OAuth2AuthorizedClientService authorizedClientService;
private final RestTemplate restTemplate;
public SchoolController(OAuth2AuthorizedClientService clientService,
RestTemplate restTemplate) {
this.authorizedClientService = clientService;
this.restTemplate = restTemplate;
}
@RequestMapping("")
public ModelAndView index() {
return new ModelAndView("index");
}
@GetMapping("/classes")
@PreAuthorize("hasAuthority('SCOPE_profile')")
public ResponseEntity<List<TeachingClassDto>> listClasses(
@AuthenticationPrincipal OAuth2AuthenticationToken authentication) {
OAuth2AuthorizedClient authorizedClient =
this.authorizedClientService.loadAuthorizedClient(
authentication.getAuthorizedClientRegistrationId(),
authentication.getName());
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
restTemplate.getInterceptors().add(getBearerTokenInterceptor(accessToken.getTokenValue()));
return restTemplate
.exchange("https://school-service/class", HttpMethod.GET, null,
new ParameterizedTypeReference<List<TeachingClassDto>>() {});
}
private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {
return (request, bytes, execution) -> {
request.getHeaders().add("Authorization", "Bearer " + accessToken);
return execution.execute(request, bytes);
};
}
}
由于  
school-ui
和  
school-service
使用相同的OIDC应用程序设置,服务器将识别并验证访问令牌(也是JWT),并允许访问。

此时,您可以选择使用

./mvnw spring-boot:run
或Docker Compose 单独运行所有应用程序 。后一种方法只需要几个命令.
mvn clean install
docker-compose down
docker-compose up -d

10.HTTP Basic Auth安全通信

为了进一步提高微服务,Eureka Server和Spring Cloud Config之间的安全性,您可以添加HTTP基本身份验证。为此,您需要在
config
和  
discovery
项目中添加
spring-boot-starter-security
依赖  。然后指定
spring.security.user.password
并加密它。您可以在Spring Cloud Config的安全文档中了解有关如何执行此操作的更多信息  。

在两个项目中配置Spring Security后,您可以调整URL以在其中包含用户名和密码。例如,

school-ui
项目中的
bootstrap.yml
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:https://username:password@localhost:8761/eureka}
还需要对
docker-compose.yml
中的URL进行类似的调整。

本教程展示了如何确保您的服务到服务通信在微服务架构中是安全的。了解如何在任何地方使用HTTPS并使用OAuth 2.0和JWT保护API。

您可以在GitHub上的oktadeveloper/okta-spring-microservices-https-example找到此示例的源代码。

出处spring for all 翻译组

8月福利,准时来袭!关注公众号

后台回复:003 ,领取7月翻译集锦~

往期福利:回复 001, 002即可领取

Spring WebClient vs. RestTemplate

● 为什么选择 Spring 作为 Java 框架?Java有损转换Java有损转换

● 追踪JVM中的本地内存

右上角按钮分享给更多人哦~

来都来了,点个在看再走吧~~~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐