内容总结自《微服务架构设计模式》
应用开发人员主要负责实现安全性的四个方面:
安全架构的一个关键部分是会话,它存储主体的ID和角色。传统的Java EE应用程序,会话是Httpsession内存中会话。会话令牌代表着每一个具体的会话,客户端在每个请求中包含会话令牌。它通常是一串无法读懂的数字标记,例如经过加密的强随机数。应用程序的会话令牌是一个名为JSESSIONID的HTTP cookie。
实现安全性的另一个关键是安全上下文,它存储有关发出当前请求的用户的信息。SpringSecurity框架使用标准的Java EE方法将安全上下文存储在静态的线程局部变量中,任何被调用以处理请求的代码都可以访问该变量。请求处理程序可以调用securityContextHolder.getContext ( ) . getAuthentication()获取有关当前用户的信息,例如他们的身份和角色。相反,Passport框架将安全上下文存储为request对象的user属性。
还有就是使用基于角色的授权。应用定义了与不同类型用户相对应的几个角色,如CONSUMER、RESTAURANT、COURIER和ADMIN。可使用Spring Security 的声明性安全机制来限制对特定角色的URL 和服务方法的访问。角色也与业务逻辑交织在一起。例如,消费者只能访问自己的订单,而管理员可以访问所有订单。
在微服务应用程序中实现安全性的一个挑战是我们不能仅仅从单体应用程序借鉴设计思路。这是因为单体应用程序的安全架构的一些方面对微服务架构来说是不可用的,例如:
1、使用API Gateway
处理身份验证有两种不同的方法。一种选择是让各个服务分别对用户进行身份验证。这种方法的问题在于它允许未经身份验证的请求进入内部网络。它依赖于每个开发团队在所有服务中正确实现安全性。因此,出现安全漏洞的风险和概率都很大。
在服务中实现身份验证的另一个问题是不同的客户端以不同的方式进行身份验证。纯API客户端使用基本身份验证为每个请求提供凭据。其他客户端可能首先登录,然后为每个请求提供会话令牌。但我们要避免在服务中处理多种不同的身份验证机制。
更好的方法是让API Gateway在将请求转发给服务之前对其进行身份验证。在APIGateway中进行集中API身份验证的优势在于只需要确保这里的验证是正确的。因此,出现安全漏洞的可能性要小得多。另一个好处是只有API Gateway需要处理各种不同的身份验证的机制
验证客户端的凭据很重要,但这还不够。应用程序还必须实现访问授权机制,以验证是否允许客户端执行所请求的操作。例如,一个接口getorderDetails()查询只能由下此order的消费者(基于实例的安全性的一个示例)和为所有消费者提供服务的客户服务代表调用。
实现访问授权的一个位置是APl Gateway。例如,它可以将对GET/orders/ {orderId)的访问限制为消费者和客户服务代表。如果不允许用户访问特定路径,则API Gateway可以在将请求转发到服务之前拒绝该请求。与身份验证一样,在API Gateway中集中实现访问授权可降低安全漏洞的风险。你可以使用安全框架(如Spring Security)在APl Gateway中实现访问授权。
在API Gateway中实现访问授权的一个弊端是,它有可能产生API Gateway与服务之间的耦合,要求它们以同步的方式进行代码更新。而且,APl Gateway通常只能实现对URL路径的基于角色的访问。由API Gateway实现对单个领域对象的访问授权通常是不实际的,因为这需要详细了解服务的领域逻辑。
另一个实现访问授权的位置是服务。服务可以对URL和服务方法实现基于角色的访问授权。它还可以实现ACL来管理对聚合的访问。例如,在order service中可以实现基于角色和基于ACL 的授权机制,以控制对order的访问。FTGO应用程序中的其他服务也可以实现类似的访问授权逻辑。
2、使用JWT传递用户身份和角色
在微服务架构中实现安全性时,你需要确定API Gateway应使用哪种类型的令牌来将用户信息传递给服务。有两种类型的令牌可供选择。一种选择是使用不透明(无可读性)的令牌,它们通常是一串UUID。不透明令牌的缺点是它们会降低性能和可用性,并增加延迟。因为这种令牌的接收方必须对安全服务发起同步RPC调用,以验证令牌并检索用户信息。
另一种消除对安全服务调用的方法是使用包含有关用户信息的透明令牌。透明令牌的一个流行的标准是JSON Web令牌(JWT)。JWT是在访问双方之间安全地传递信息(例如用户身份和角色)的标准方式。JWT 的内容包含一个JSON对象,其中有用户的信息,例如其身份和角色,以及其他元数据,如到期日期等。它使用仅为JWT的创建者所知的数字签名,例如API Gateway和JWT 的接收者(服务)。该签名确保恶意第三方不能伪造或篡改JWT。
3、使用OAuth 2.0
假设你要为FTGO应用程序实现一个User Service,该应用程序管理包含用户信息(如凭据和角色)的数据库。API Gateway调用User Service来验证客户端请求并获取JWT。你可以设计User Service的API并使用你喜欢的Web框架实现它。但这不是FTGO应用程序特有的通用功能,自己开发此类服务往往是得不偿失的。
幸运的是,你不需要开发这种安全基础设施。你可以使用名为OAuth 2.0的标准的现成服务或框架。OAuth 2.0是一种访问授权协议,最初旨在使公共云服务(如GitHub或Google)的用户能够授予第三方应用程序访问其信息的权限,而不必向第三方应用透露他们的密码。例如,OAuth 2.0使你能够安全地授予第三方基于云的持续集成(CI)服务,访问你的GitHub存储库。
OAuth 2.0中关键概念如下:
使用OAuth2.0的一一个重要好处是它是经过验证的安全标准。使用现成的OAuth2.0身份验证服务器意味着你不必浪费时间重新发明轮子或者是没有开发不安全的设计的风险。但OAuth 2.0不是在微服务架构中实现安全性的唯一方法。 无论你使用哪种方法,三个关键思
想如下:
假设你负责开发order History Service。该服务使用来自ApacheKafka的事件并读取和写入AWS DynamoDB表项。为了运行此服务,它需要各种配置属性,包括Apache Kafka的网络位置以及AWS DynamoDB的访问凭据和网络位置。
这些配置属性的值取决于运行服务的环境。例如,开发环境和生产环境使用的ApacheKafka代理和AWS 访问凭据肯定不同。将特定环境的配置属性值硬写人可部署服务的代码中是没有意义的,因为这些环境都是动态创建的。相反,服务应该由部署流水线构建一次,并自动部署到多个环境中。
将几套可能的配置属性集硬写入源代码中,然后使用例如Spring Framework 的配置文件机制在运行时选择,这样做也没有意义。因为这样做会引入安全漏洞,并限制可以部署的位置。此外,应该使用秘密存储机制安全地存储凭据等敏感数据。因此,你应该使用外部化配置模式为服务在运行时提供适当的配置属性。
外部化配置机制在运行时向服务实例提供配置属性值。主要有两种方法:
1、推送模式
Spring Boot从各种来源读取属性。我发现以下来源在微服务架构中很有用:
命令行参数。
SPRING_APPLICATION_JSON,包含JSON的操作系统环境变量或JVM系统属性。
JVM 系统属性。
操作系统环境变量。
当前目录中的配置文件。
来自此列表中靠前的来源的特定属性值将覆盖此列表中稍后的来源中的相同属性。例如,操作系统环境变量会覆盖从配置文件中读取的属性。
2、拉取模式
有多种方法可以实现配置服务器,包括:
Spring Cloud Config项目是一个优秀的基于配置服务器的框架。它由服务器和客户端组成。服务器支持各种后端,用于存储配置属性,包括版本控制系统、数据库和 HashicorpVault。客户端从服务器检索配置属性并将它们注入Spring ApplicationContext。
使用配置服务器有几个好处:
1、健康检查API
公开返回服务运行状况的接口。应用需要有一个探活接口,用于证明当前应用是否正常。
2、日志聚合分
记录服务活动并将日志写入集中式日志记录服务器,该服务器提供搜索和告警。排除故障,必不可少,如阿里云SLS、ELK搭建日志平台
3、布式跟踪
为每一个在服务之间跳转的外部请求分配唯一ID,并跟踪请求。如鹰眼、Zipkin
4、应用程序指标
服务运维指标,例如计数器和指标,并将它们公开给指标服务器。比如CPU、内存占用率,服务RT、QPS。可以配置相关的应用告警,提前防范风险
5、异常跟踪
向异常跟踪服务报告异常,该异常跟踪服务可以对异常进行重复数据删除,向开发人员发出警报并跟踪每个异常的解决方案
服务很少记录异常,当它发生异常时,确定根本原因很重要。异常可能是失败或编程错误的结果。查看异常的传统方法是查看日志。你甚至可以配置日志记录服务器,以便在日志文件中出现异常时向你发出警报。但是,这种方法存在一些问题:
6、审核日志记录
记录数据库中的用户操作,以帮助客户支持、确保合规性,并检测可疑行为。
不同的实现审计日志:
微服务基底是解决各种共性问题的好方法,例如断路器。但使用微服务基底的一个障碍是,你需要确保你使用的编程语言有对应的微服务基底框架或类库。例如,如果你是Java/Spring的开发人员,Spring Boot和 Spring Cloud很有用,但如果你想编写基于Node.js的服务,它们就没有任何帮助。
避免此问题的新兴替代方案是在所谓的服务网格中实现服务之外的某些功能。服务网格是网络基础设施,它调和(mediate)服务与其他服务和外部应用程序之间的通信。进出服务的所有网络流量都通过服务网格。它实现了各种共性的需求:包括断路器、分布式追踪、服务发现、负载均衡和基于规则的流量路由。服务网格还可以通过在服务之间使用基于TLS的机制来保护进程间通信。因此,你不再需要在服务中解决这些特定问题。
使用服务网格后,微服务基底需要担负的责任就少了很多。它只需要实现与应用程序代码紧密集成的问题,例如外部化配置和健康检查。
当前的服务网格实现方案: