微服务安全与可观测性全解析
1. 使用JWT访问受保护资源
当从OAuth 2.0授权服务器获取到JWT访问令牌后,可使用以下cURL命令访问受保护资源。请确保将
$TOKEN
替换为有效的访问令牌:
curl -k -H "Authorization: Bearer $TOKEN" https://localhost:9443/order/11
响应示例:
{"customer_id":"101021","order_id":"11","payment_method":{"card_type":"VISA",
"expiration":"01/22","name":"John Doe","billing_address":"201, 1st Street,
San Jose, CA"},"items":[{"code":"101","qty":1},{"code":"103","qty":5}],
"shipping_address":"201, 1st Street, San Jose, CA"}
2. 控制微服务的访问
控制微服务的访问有多种方式,下面将介绍基于范围和用户角色的访问控制。
2.1 基于范围的访问控制
-
获取带范围的JWT令牌
:使用以下命令获取具有
foo范围的JWT访问令牌,需正确替换$CLIENTID和$CLIENTSECRET的值,并确保授权服务器sample01正在运行。
curl -v -X POST --basic -u $CLIENTID:$CLIENTSECRET -H "Content-Type:
application/x-www-form-urlencoded;charset=UTF-8" -k -d "grant_type=client_
credentials&scope=foo" https://localhost:8443/oauth/token
-
启用范围访问控制
:在订单处理微服务
sample02中,需在sample02/src/main/java/com/apress/ch12/sample02/OrderProcessingApp.java类中添加@EnableGlobalMethodSecurity注解。
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableResourceServer
public class OrderProcessingApp {
}
-
方法级范围控制
:在
sample02/src/main/java/com/apress/ch12/sample02/service/OrderProcessing.java类的方法上使用@PreAuthorize注解,要求访问该方法需要bar范围。
@PreAuthorize("#oauth2.hasScope('bar')")
@RequestMapping(value = "/{id}", method= RequestMethod.GET)
public ResponseEntity<?> getOrder(@PathVariable("id") String orderId) {
}
-
测试范围控制
:使用仅具有
foo范围的JWT访问令牌执行以下命令,会因令牌不包含所需范围而报错。
curl -k -H "Authorization: Bearer $TOKEN" https://localhost:9443/order/11
响应:
{"error":"access_denied","error_description":"Access is denied"}
2.2 基于角色的访问控制
-
获取带角色的JWT令牌
:使用密码授权类型获取JWT访问令牌,需正确替换
$CLIENTID、$CLIENTSECRET、$USERNAME和$PASSWORD的值。
curl -v -X POST --basic -u $CLIENTID:$CLIENTSECRET -H "Content-Type:
application/x-www-form-urlencoded;charset=UTF-8" -k -d "grant_type=password
&username=$USERNAME&password=$PASSWORD&scope=foo" https://localhost:8443/
oauth/token
-
启用角色访问控制
:同样在
OrderProcessingApp.java类中添加@EnableGlobalMethodSecurity注解。
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableResourceServer
public class OrderProcessingApp {
}
-
方法级角色控制
:在
OrderProcessing.java类的方法上使用@PreAuthorize注解,要求访问该方法需要USER角色。
@PreAuthorize("hasRole(USER)")
@RequestMapping(value = "/{id}", method= RequestMethod.GET)
public ResponseEntity<?> getOrder(@PathVariable("id") String orderId) {
}
-
测试角色控制
:使用具有
foo范围且颁发给具有USER角色用户的JWT访问令牌执行以下命令,应能成功响应。
curl -k -H "Authorization: Bearer $TOKEN" https://localhost:9443/order/11
2.3 范围和角色组合控制
若要同时基于范围和角色控制对方法的访问,可在相应方法上使用以下注解:
@PreAuthorize("#oauth2.hasScope('bar') and hasRole('USER')")
3. 保障服务间通信安全
保障服务间通信安全有两种方法,分别基于JWT和TLS相互认证。
3.1 基于JWT的服务间通信
在该场景中,订单处理微服务接收到订单后会与库存微服务通信以更新库存,会将获取的访问令牌传递给库存微服务。
-
库存微服务配置
:库存微服务代码位于
ch12/sample03
目录,要启用JWT认证,需在
sample03/src/main/resources/application.properties
文件中取消以下属性的注释。
security.oauth2.resource.jwt.keyUri: https://localhost:8443/oauth/token_key
-
启动库存微服务
:在
sample03目录下使用以下Maven命令启动服务,服务将在HTTPS端口10443启动。
mvn spring-boot:run
-
端到端流程测试
:先获取具有
foo和bar范围的JWT访问令牌:
curl -v -X POST --basic -u $CLIENTID:$CLIENTSECRET -H "Content-Type:
application/x-www-form-urlencoded;charset=UTF-8" -k -d "grant_type=password
&username=$USERNAME&password=$PASSWORD&scope=foo bar"
https://localhost:8443/oauth/token
然后使用该令牌向订单处理微服务下单:
curl -v -k -H "Authorization: Bearer $TOKEN" -H "Content-Type:
application/json" -d '{"customer_id":"101021","payment_method":{"card_type":
"VISA","expiration":"01/22","name":"John Doe","billing_address":"201, 1st
Street, San Jose, CA"},"items":[{"code":"101","qty":1},{"code":"103",
"qty":5}],"shipping_address":"201, 1st Street, San Jose, CA"}' https://
localhost:9443/order
若一切正常,cURL客户端应显示
201
HTTP状态码,库存微服务终端将打印订单号。
3.2 基于TLS相互认证的服务间通信
TLS相互认证常用于服务器到服务器的认证,JWT用于在微服务之间传递用户上下文。
-
密钥库和信任库要求
:每个服务都需有自己的密钥库
keystore.jks
和信任库
trust-store.jks
。例如,订单处理微服务与库存微服务通信时,库存微服务的信任库
sample03/trust-store.jks
中需包含订单处理微服务公钥的证书颁发机构的公共证书;订单处理微服务的信任库
sample02/trust-store.jks
中需包含库存微服务公钥的证书颁发机构的公共证书。
-
订单处理微服务配置
:在
sample02/src/main/java/com/apress/ch12/sample02/OrderProcessingApp.java
文件中设置以下属性:
System.setProperty("javax.net.ssl.trustStore", path + File.separator +
"trust-store.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "springboot");
System.setProperty("javax.net.ssl.keyStore", path + File.separator +
"keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "springboot");
-
库存微服务配置
:在
sample03/src/main/java/com/apress/ch12/sample03/InventoryApp.java文件中设置信任库属性:
System.setProperty("javax.net.ssl.trustStore", path + File.separator +
"trust-store.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "springboot");
在
sample03/src/main/resources/application.properties
文件中设置以下属性以启用TLS相互认证:
server.ssl.client-auth:need
- 端到端流程测试 :使用有效访问令牌调用订单处理微服务进行测试:
curl -v -k -H "Authorization: Bearer $TOKEN" -H "Content-Type:
application/json" -d '{"customer_id":"101021","payment_method":{"card_type":
"VISA","expiration":"01/22","name":"John Doe","billing_address":"201,
1st Street, San Jose, CA"},"items":[{"code":"101","qty":1},{"code":"103",
"qty":5}],"shipping_address":"201, 1st Street, San Jose, CA"}'
https://localhost:9443/order
4. 保障执行器端点安全
Spring Boot通过执行器提供了开箱即用的监控功能。
- 启用执行器端点 :在任何Spring Boot微服务中,添加以下依赖以激活执行器端点。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
-
启动微服务
:在
ch12/sample04目录下使用以下命令启动订单处理微服务,服务将在HTTPS端口8443启动。
mvn spring-boot:run
- 未安全配置时的测试 :执行以下cURL命令可成功获取响应。
curl -k https://localhost:8443/health
响应:
{"status":"UP"}
-
启用安全配置
:在
sample04/pom.xml文件中添加以下依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
-
添加用户和角色
:在
sample04/src/main/java/com/apress/ch12/sample04/config/SecurityConfig.java文件中取消@Configuration类级注解的注释,并添加以下代码,为系统引入一个名为admin且具有ACTUATOR角色的用户。
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin")
.password("admin").roles("ACTUATOR");
}
-
重启服务并测试
:重启服务后,使用
admin/admin凭证执行以下cURL命令。
curl –k --basic -u admin:admin https://localhost:8443/health
响应:
{"status":"UP"}
5. 可观测性概述
收集数据成本低,但在需要时缺乏数据可能代价高昂。可观测性是衡量从系统外部输出推断其内部状态的程度,是微服务设计中重要的一环,需要跟踪每个微服务的吞吐量、请求成功/失败数量、CPU和内存等资源利用率以及一些业务相关指标。
6. 可观测性的三大支柱
可观测性可通过日志记录、指标和跟踪这三种方式实现,它们也被称为可观测性的三大支柱。
| 支柱 | 描述 | 示例 |
| — | — | — |
| 日志记录 | 记录事件,可包含事务的时间戳、状态、发起者等元数据 | 记录每个通过微服务的事务 |
| 指标 | 由事件测量数据组合得出,反映服务运行状况,可用于设置警报 | 单位时间内处理的事务数量、事务成功率/失败率、平均延迟 |
| 跟踪 | 从日志中派生,考虑事件顺序和相互影响,有助于找出问题根源 | 跟踪计费微服务请求失败的原因到订单处理微服务 |
7. 使用Spring Cloud Sleuth进行分布式跟踪
分布式跟踪有助于跟踪跨越多个微服务的请求,在处理客户端的单个请求时,通常会涉及多个微服务。它不仅对微服务有价值,对任何分布式系统都很有用,能帮助识别和隔离与延迟、消息丢失、吞吐量等相关的问题。
graph LR
A[客户端请求] --> B[订单处理微服务]
B --> C[库存微服务]
B --> D[其他微服务]
C --> E[更新库存]
D --> F[执行其他操作]
通过以上介绍,我们了解了微服务安全和可观测性的相关知识,包括访问控制、服务间通信安全保障以及可观测性的实现方法,这些技术对于构建健壮、安全和可监控的微服务系统至关重要。
微服务安全与可观测性全解析
8. 构建分布式跟踪系统
为了更好地实现分布式跟踪,我们可以使用 Spring Cloud Sleuth、Zipkin 和 Jaeger 来构建一个完整的分布式跟踪系统。以下是构建该系统的详细步骤:
8.1 引入依赖
在项目的
pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
8.2 配置 Zipkin 服务器
Zipkin 是一个开源的分布式跟踪系统,用于收集和展示跟踪数据。可以通过 Docker 快速启动一个 Zipkin 服务器:
docker run -d -p 9411:9411 openzipkin/zipkin
8.3 配置应用程序
在应用程序的配置文件(如
application.properties
)中添加以下配置,将跟踪数据发送到 Zipkin 服务器:
spring.zipkin.base-url: http://localhost:9411
spring.sleuth.sampler.probability: 1.0
8.4 启动应用程序
启动微服务应用程序,Spring Cloud Sleuth 会自动为每个请求生成唯一的跟踪 ID 和跨度 ID,并将这些信息传递给下游服务。
8.5 查看跟踪数据
打开浏览器,访问
http://localhost:9411
,可以看到 Zipkin 的界面。在界面中可以搜索和查看各个请求的跟踪信息,包括请求的路径、时间、延迟等。
9. 可视化、监控和警报
使用 Prometheus 和 Grafana 可以实现对微服务的可视化、监控和警报功能。以下是具体的操作步骤:
9.1 安装和配置 Prometheus
Prometheus 是一个开源的监控系统,用于收集和存储指标数据。可以通过 Docker 快速启动一个 Prometheus 服务器:
docker run -d -p 9090:9090 prom/prometheus
在
prometheus.yml
配置文件中添加要监控的目标:
scrape_configs:
- job_name: 'my_microservice'
static_configs:
- targets: ['localhost:8080']
9.2 安装和配置 Grafana
Grafana 是一个开源的可视化工具,用于展示 Prometheus 收集的指标数据。可以通过 Docker 快速启动一个 Grafana 服务器:
docker run -d -p 3000:3000 grafana/grafana
打开浏览器,访问
http://localhost:3000
,使用默认的用户名和密码(
admin/admin
)登录 Grafana。在 Grafana 中添加 Prometheus 作为数据源,并创建仪表盘来展示指标数据。
9.3 设置警报
在 Grafana 中可以设置警报规则,当指标数据超过预设的阈值时,会触发警报。可以通过配置通知渠道(如邮件、Slack 等)来接收警报信息。
10. 总结
微服务的安全和可观测性是构建健壮、可靠的微服务系统的关键。通过本文的介绍,我们了解了以下重要内容:
- 访问控制 :可以基于范围和角色对微服务的访问进行控制,确保只有授权的用户和服务可以访问受保护的资源。
- 服务间通信安全 :可以使用 JWT 和 TLS 相互认证来保障服务间通信的安全,防止数据泄露和中间人攻击。
- 可观测性 :通过日志记录、指标和跟踪这三大支柱,可以实现对微服务的全面监控和分析,及时发现和解决问题。
- 分布式跟踪系统 :使用 Spring Cloud Sleuth、Zipkin 和 Jaeger 可以构建一个完整的分布式跟踪系统,帮助我们跟踪和分析跨越多个微服务的请求。
- 可视化、监控和警报 :使用 Prometheus 和 Grafana 可以实现对微服务的可视化、监控和警报功能,及时发现系统的异常情况。
在实际应用中,我们可以根据具体的需求和场景选择合适的技术和工具,确保微服务系统的安全和可观测性。
graph LR
A[客户端] --> B[微服务系统]
B --> C[访问控制]
B --> D[服务间通信安全]
B --> E[可观测性]
C --> F[范围控制]
C --> G[角色控制]
D --> H[JWT通信]
D --> I[TLS认证]
E --> J[日志记录]
E --> K[指标分析]
E --> L[跟踪系统]
L --> M[Spring Cloud Sleuth]
L --> N[Zipkin]
L --> O[Jaeger]
E --> P[可视化监控]
P --> Q[Prometheus]
P --> R[Grafana]
通过以上的流程图,我们可以更清晰地看到微服务系统中各个组件之间的关系和作用。在实际开发和运维过程中,我们可以根据这个架构来构建和管理微服务系统,确保系统的安全和稳定运行。
微服务安全与可观测性实践
超级会员免费看
993

被折叠的 条评论
为什么被折叠?



