读书笔记《building-a-restful-web-service-with-spring》应对安全问题
第 7 章。处理安全性
安全性跨越 IT 系统的每一个边界;从物理访问数据中心和服务器机架,到加密通信,一直到验证 Web 服务端点的输入。在本章中,我们将重点关注直接影响 Web 服务的安全措施。我们将涵盖以下主题:
我们的示例 RESTful Web 服务的预订组件用于说明如何使用 Spring 解决安全问题
认证技术
授权技术
输入验证
加密的使用
预订服务
在深入研究如何使用 Spring 处理安全性之前,让我们首先讨论我们将在本章中使用的示例物业管理系统的组件:预订服务。
顾名思义,这个 组件将提供在我们的示例物业管理系统中接受和管理预订的必要功能。让我们考虑以下 Java 接口:
这种抽象允许 我们进行和检索预订。假设此接口的实现可用,我们现在可以将注意力转向构建必要的 RESTful 端点以公开此功能。
REST 资源
正如我们在 第 2 章中看到的,使用 Maven 和 Gradle 构建 RESTful Web 服务,我们可以公开检索的能力带有以下代码的标识符预订:
在以下部分中,我们将了解如何将安全性应用于此端点。
验证
身份验证涉及确保用户是他们所说的那样。有多种方法可以 对用户进行身份验证。本节将描述 HTTP 提供的一些机制。
HTTP 基本身份验证
这是 HTTP 规范中最简单的身份验证形式。它依赖于将用户名和密码组合作为 Authorization
标头传递给任何要求 身份验证的 HTTP 请求.
当客户端向需要身份验证的端点发出请求时,服务器将以 HTTP 401 Not Authorized 响应进行响应。响应将包含以下标头:
此标头指示客户端必须使用基本方案对用户进行身份验证。现代浏览器会在收到这样的响应时自动提示用户提供他们的凭据,并使用 Authorization
标头重新发出请求。此标头应包含在< 中编码的用户名和密码组合(格式为username:password
)的方案/a> Base64
。例如,让我们考虑一个包含以下标头的请求:
解码后,服务器将需要使用用户名 rest
和密码 rocks
检查用户。
小费
虽然这个方案很容易实施,但没有提供保密措施来保护凭证。实际上,凭证只是经过编码而不是加密,并且可以很容易地访问。因此,此方案必须与 HTTPS 一起使用才能被认为是安全的。
使用 Spring 的基本身份验证
让我们看看我们如何 设置一个 Spring RESTful Web 服务来支持基本身份验证。首先,我们需要为我们的项目添加一些新的依赖项:
这将导入 Spring 的 web 安全模块,以及它的配置支持。下一步是 配置安全性。我们可以在 Java 中通过声明以下类来做到这一点:
这是一个非常简单的配置,不是生产就绪的,但足以简单地说明 Spring Security 的配置。我们使用用户名 rest
和密码 rocks
定义一个用户。我们还指示 Spring 使用 HTTP Basic 身份验证方案对所有请求进行身份验证。
小费
在现实世界的系统中,用户不会被存储在内存中(如本例中的 auth.inMemoryAuthentication()
所示),而是在密码加密的数据库中 使用例如 Bcrypt 加密(https://en.wikipedia.org/wiki/Bcrypt)。
剩下的最后一步是让我们的 Web 服务使用这个安全配置。我们可以通过扩展 org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer
来做到这一点,如下所示:
HTTP 摘要式身份验证
与 HTTP Basic 身份验证方案类似,HTTP Digest
身份验证方案使用用户名和密码来对用户进行身份验证。但是,与之前的方案不同, 凭证不是以易于解码的格式通过网络发送的。取而代之的是用户名、密码和一些额外部分的MD5散列
的信息被发送。当服务器收到请求时,它会使用相同的算法生成另一个哈希值,并比较 这两个值。如果它们匹配,则用户输入了正确的密码。如果不是,则身份验证失败,将返回相应的状态码。
要在 Spring 中设置 Digest
身份验证,必须配置 org.springframework.security.web.authentication.www.DigestAuthenticationFilter
过滤器.此过滤器将发出身份验证标头,例如:
过滤器还将处理请求中的任何 Authorization
标头。例如:
在访问安全资源时,服务会在 401 响应中发送带有方案、领域和额外信息的 WWW-Authenticate
标头。浏览器将通过提示用户并使用包含 < 的 MD5 哈希的 Authorization
标头重新发出请求来处理此问题/a>用户的凭据。
要启用 Digest
身份验证,让我们修改我们的 SecurityConfig
类:
通过这个更新的实现,我们将 基本身份验证替换为更安全的Digest
身份验证方案。
笔记
虽然 HTTP Digest
身份验证提供了一种通过其他 非安全网络发送加密凭据的机制,但阅读器应该知道 MD5 算法有已知的限制,应该仔细考虑。更多信息可以在 https://en.wikipedia.org/wiki/Digest_access_authentication< /a>。
基于令牌的身份验证
我们将简要讨论的最后一个方案是基于令牌的身份验证。这种方法不是 HTTP 规范的一部分,而是一种常见的 身份验证方法。该方案依赖于服务器发布的加密令牌,然后在客户端的每个请求上发送。令牌可以使用现代、强大的加密算法进行加密。 Spring 使用 org.springframework.security.core.token.TokenService
和 org.springframework.security 为这种方案提供了很好的支持。 core.token.Token
。
比如服务开发者可以看看org.springframework.security.core.token.KeyBasedPersistenceTokenService
有关此身份验证方法的示例使用。
其他认证方式
如果以前的身份验证方法不适合,Web 服务设计人员可能有兴趣研究 使用以下内容:
OAuth2:这是一种开源授权标准,可为客户端应用程序提供 代表 用户安全访问服务器资源。当用户希望通过第三方应用 登录服务时,这一点尤其受欢迎。查看 http://oauth.net/2/ 了解更多详情。
JWT:JSON Web Token 是一个用于检查请求中发送的信息是否可以使用数字签名进行验证和信任。更多关于JWT的信息可以在http找到://jwt.io。
介绍了身份验证之后,我们现在可以专注于如何管理授权。
授权
身份验证的必然结果是授权。这两个概念通常一起处理,但它们指的是保护 Web 服务的两个不同要求。身份验证验证用户的身份,而授权管理用户有权 执行哪些操作。授权通常依赖于将用户与角色相关联并控制允许哪些用户角色执行特定操作。
使用 Spring 进行授权
网址映射
资源注释
以下部分提供了这两种方法的说明。
网址映射
扩展我们之前的 示例,我们可以修改 SecurityConfig
以声明细粒度 URL 映射,如下所示:
在这个新版本中,我们指示 Spring 只允许管理员访问读取预订。所有其他端点都接受来自具有任何角色的经过身份验证的用户的请求。为了能够测试这个安全配置,让我们添加一个管理员用户。我们可以通过如下修改我们的类来做到这一点:
除了我们的 rest
用户,我们现在还声明了一个管理员用户,用户名 admin
和密码 管理员
。
使用这个新配置,如果我们以 USER 身份登录并尝试访问预订,服务器将生成 403 Forbidden 响应。在本地,您可以通过在 Web 浏览器中打开 http://localhost:8080/bookings/1
来测试此行为。
笔记
由于浏览器使用 HTTP Basic
或 HTTP Digest
身份验证方案缓存凭据,因此注销有点棘手,并且通常需要关闭浏览器.值得庆幸的是,现代浏览器允许隐私浏览。此功能可以帮助加快开发和测试 Web 服务的安全性。
这种方法有助于在全球范围内保护 Web 服务,但在需要细粒度授权时可能会很麻烦。实际上,配置(Java 或 XML)可能会变得难以 维护。下一节将描述一种更具可扩展性的方法。
资源注释
与上一节中讨论的集中式配置不同,使用注释允许直接在资源类中控制资源访问。 Spring 提供 使用org.springframework.security.access.prepost.PreAuthorize< /代码>。
让我们修改本章开头描述的端点:
将 @PreAuthorize("hasRole('ADMIN')")
添加到我们的端点声明中会指示 Spring仅向管理员授予对该资源的访问权限的安全性。如果用户尝试调用此端点,服务器将生成 403 Forbidden
响应。
小费
如果资源必须可供多个角色访问,则可以使用以下表达式:hasAnyRole('role1, role2')
。
现在让我们将注意力转向安全的另一个方面,它也起着重要的作用:输入验证。下一节将介绍如何使用 Spring 实现输入验证。
输入验证
除了身份验证和授权之外,构建安全 Web 服务的一个重要领域是确保始终验证输入。除了维护数据完整性之外,这样做还可以防止 SQL 注入等安全漏洞。
Java Bean 注解
为了实现输入验证,我们可以使用 JavaEE 6 中引入的 Java Bean 验证注解。为了说明它们的使用,让我们在 中实现端点以接受预订示例网络服务。我们的预订服务接受以下 Java 类形式的 请求:
您可以在这里看到 @javax.validation.constraints.Min、@javax.validation.constraints.NotNull
和 @javax.validation 的使用。约束。大小
。 @Min
注释允许定义 roomId
的最小有效值。 @NotNull
注释确保该字段具有值。最后,@Size
注释有助于确保客户姓名不大于数据库字段的大小。
概括
服务设计者可能需要实施的安全措施的广度比本章所涵盖的要广泛得多。从认证用户到授权他们的操作,以及使用加密来防止窃听敏感信息,我们已经介绍了使用 Spring 保护 Web 服务的基本原则。
在本章中考虑了安全问题之后,评估 RESTful Web 服务的生产就绪性的另一个关键属性是测试。
在下一章中,我们将研究可以利用哪些工具和技术来验证我们的示例 RESTful Web 服务是否表现出预期的行为。