oauth单点退出原理,动手操作一个OAuth
oauth单点退出原理,动手操作一个OAuth启动 Keycloak 服务器后,在浏览器 http://localhost:8080 中访问它。在 Keycloak 的第一页中,您可以通过输入用户名和密码来配置一个 admin 帐户( 图 4 )。要安装 Keycloak ,您只需下载包含官方网站 https://www.keycloak.org/downloads 的最新版本的存档。 然后,解压缩文件夹中的存档,您可以使用您在 bin 文件夹中找到的独立可执行文件启动 Keycloak 。 如果您正在使用 Linux,则需要运行 StandalOle.sh。 对于 Windows,您可以运行 Standalone.bat。Keycloak 的配置很灵活,尽管它可以变得复杂,这取决于你想要实现什么。在本文中,我们只讨论我们的示例需要进行的设置。我们的设置仅定义了少数用户的角色。但 Keycloak 能做的远不止这些。如果您计划在实际场
接上一篇《动手操作:一个 OAuth 2 应用程序(1) - 应用程序场景 》进行场景分析后,本篇就开始动手实现授权服务器。
在本文中,我们将 Keycloak 配置为系统的授权服务器 ( 图 3 )。Keycloak 是一个优秀的开源工具,专为身份和访问管理而设计。你可以从 keycloak.org 下载 Keycloak 。Keycloak 提供了本地管理简单用户的能力,还提供了高级功能,如用户联盟。您可以将其连接到 LDAP 和 Active Directory 服务或不同的身份提供者。例如,您可以使用 Keycloak 作为高级身份认证层,将其连接到我们在《 OAuth 2 是如何工作的?——(1)》中讨论过的常见 OAuth 2 提供者之一。
图 3
图 3 作为本文中实际应用程序的一部分,我们遵循三个主要步骤。在本节中,我们将 Keycloak 配置为系统的授权服务器作为第一步。
Keycloak 的配置很灵活,尽管它可以变得复杂,这取决于你想要实现什么。在本文中,我们只讨论我们的示例需要进行的设置。我们的设置仅定义了少数用户的角色。但 Keycloak 能做的远不止这些。如果您计划在实际场景中使用 Keycloak ,我建议您首先阅读其官方网站上的详细文档 : https://www.keycloak.org/documentation。在 Ken Finnigan (Manning 2018)的《企业Java微服务》(Enterprise Java Microservices) 的第9章中,您还可以找到作者使用 Keycloak 进行用户管理的关于保护微服务的很好的讨论。这是链接:
https://livebook.manning.com/book/enterprise-Java-microservices/chapter-9
( 如果你喜欢关于微服务的讨论,我推荐你阅读 Ken Finnigan 的整本书。作者对任何用 Java实现微服务的人都应该知道的主题提供了极好的见解。)
要安装 Keycloak ,您只需下载包含官方网站 https://www.keycloak.org/downloads 的最新版本的存档。 然后,解压缩文件夹中的存档,您可以使用您在 bin 文件夹中找到的独立可执行文件启动 Keycloak 。 如果您正在使用 Linux,则需要运行 StandalOle.sh。 对于 Windows,您可以运行 Standalone.bat。
启动 Keycloak 服务器后,在浏览器 http://localhost:8080 中访问它。在 Keycloak 的第一页中,您可以通过输入用户名和密码来配置一个 admin 帐户( 图 4 )。
图 4
图 4 要管理 Keycloak,首先需要设置管理员凭证。你可以在第一次启动 Keycloak 的时候访问它。
图 5
图 5 设置好管理帐户后,就可以使用刚才设置的凭证登录 Keycloak 管理控制台
就是这样。您成功地设置了管理凭证。然后,使用凭证登录来管理 Keycloak ,如图 5 所示。
在管理控制台中,您可以开始配置授权服务器。我们需要知道 Keycloak 暴露了哪些 OAuth 2 相关的端点。您可以在 Realm Settings 页面的 General 部分中找到这些端点,这是登录到 Administration Console 后的第一个页面 ( 图 6 )。
图 6
图 6 通过单击 OpenID Endpoint Configuration 链接可以找到与授权服务器相关的端点。您需要这些端点来获取访问令牌并配置资源服务器。
在下一个代码片段中,我提取了 OAuth 2 配置的一部分,您可以通过单击 OpenID Endpoint configuration 链接找到这些配置。此配置提供令牌端点、授权端点和支持的授权类型列表。这些细节你应该很熟悉,就像我们在前面 OAuth 2 相关文章中讨论过的那样。
{
"issuer":
"http://localhost:8080/auth/realms/master"
"authorization_endpoint":
"http://localhost:8080/auth/realms/master/
➥ protocol/openid-connect/auth"
"token_endpoint":
"http://localhost:8080/auth/realms/master/
➥ protocol/openid-connect/token"
"jwks_uri":
"http://localhost:8080/auth/realms/master/protocol/
➥ openid-connect/certs"
"grant_types_supported":[
"authorization_code"
"implicit"
"refresh_token"
"password"
"client_credentials"
]
...
}
图 7
图 7 为了测试应用程序,我们手动生成访问令牌,我们用它来调用端点。如果您为令牌定义了一个较短的生命周期,那么您需要更频繁地生成它们,当一个令牌在您可以使用它之前过期时,您可能会感到烦恼。
您可能会发现,如果您配置了长期的访问令牌,那么测试应用程序会更加方便 (图 7 )。然而,在现实场景中,请记住不要让您的令牌拥有很长的寿命。例如,在生产系统中,令牌应该在几分钟内过期。但为了进行测试,你可以让它保持活跃一天。您可以从令牌选项卡更改令牌的生命周期长度,如图 8 所示。
图 8
图 8 如果发出的访问令牌没有很快过期,您可能会发现测试更方便。您可以在 Tokens 选项卡中更改其生命周期。
现在我们已经安装了 Keycloak,设置了管理凭证,并做了一些调整,我们可以配置授权服务器了。下面是配置步骤的列表。
- 为系统注册客户端。OAuth 2 系统需要至少一个被授权服务器识别的客户端。客户端为用户发出认证请求。在 1 节中,您将学习如何添加一个新的客户端注册。
- 定义客户端作用域 ( scope )。客户端作用域标识了客户端在系统中的用途。我们使用客户端作用域定义来定制授权服务器发出的访问令牌。在第 2 节中,您将学习如何添加客户端作用域,在第 4 节中,我们将配置它以定制访问令牌。
- 为我们的应用程序添加用户。为了调用资源服务器上的端点,我们需要应用程序的用户。在 3 节中,您将学习如何添加 Keycloak 管理的用户。
- 定义用户角色和自定义访问令牌。添加用户之后,您可以为他们颁发访问令牌。您将注意到,访问令牌没有我们完成我们的场景所需的所有信息。在 4 节中,您将学习如何为用户配置角色,并定制访问令牌,以显示我们将使用 Spring Security 实现的资源服务器所期望的详细信息。
在本节中,我们将讨论在使用 Keycloak 作为授权服务器时注册客户端。与任何其他 OAuth 2 系统一样,我们需要在授权服务器注册客户端应用程序。要添加新客户端,我们使用 Keycloak 管理控制台。如图 9 所示,通过导航到左侧菜单上的 Clients 选项卡,您可以找到一个客户端列表。从这里,您还可以添加一个新的客户端注册。
图 9
图 9 要添加一个新客户端,您可以使用左侧菜单上的 Clients 选项卡导航到客户端列表。在这里,您可以通过单击 Clients 表右上角的 Create 按钮来添加一个新的客户端注册。
我添加了一个名为 fitnessapp 的新客户端。这个客户端表示允许从资源服务器调用端点的应用程序,我们将在《动手操作:一个 OAuth 2 应用程序(3)》中实现。图 10 显示了添加客户端的表单。
图 10
图 10 添加客户端时,只需要给它分配一个唯一的 Client ID ( fitnessapp ),然后单击 Save
2 指定客户端作用域在本节中,我们将为在 1 节中注册的客户端定义一个作用域。客户端作用域确定了客户的目的。我们还将在 4 节中使用客户端作用域来定制 Keycloak 发出的访问令牌。要向客户端添加作用域,我们再次使用 Keycloak 管理控制台。如图 11 所示,当从左侧菜单导航到 Client Scopes 选项卡时,您会发现一个客户端作用域列表。从这里,您还可以向列表添加一个新的客户端作用域。
通过导航到 “Client Scopes” 选项卡,可以找到所有客户端作用域的列表。
图 11
图 11 对于所有客户作用域的列表,导航到客户作用域选项卡。在这里,您可以通过单击 Client Scopes 表右上角的 Create 按钮来添加一个新的客户端作用域。
对于我们在这个实际示例中构建的应用程序,我添加了一个名为 fitnessapp 的新客户端用域。在添加新作用域时,还要确保为其设置客户端作用域的协议是 openid-connect ( 图 12 )。
注意
您可以选择的另一个协议是 SAML 2.0。Spring Security 以前为这个协议提供了一个扩展,你仍然可以在https://projects.spring.io/Spring-Security-saml/# 快速启动。我们在本系列文章中没有讨论使用 SAML 2.0,因为它不再为 Spring Security 积极开发。此外,在应用程序中,SAML 2.0 比 OAuth 2 更不常见。
图 12
图 12 添加新的客户端作用域时,给它一个唯一的名称,并确保为所需的协议定义它。在我们的例子中,我们想要的协议是 openid-connect。
创建新角色后,将其分配给客户端,如图 13 所示。通过导航到 Clients 菜单,然后选择 Client Ccopes 选项卡,可以进入这个屏幕。
图 13
图 13 一旦你有了一个客户端作用域,你将它分配给一个客户端。在此图中,我已经将需要的作用域移动到右侧名为 Assigned Default Client Scopes 的框中。通过这种方式,您现在可以将定义的作用域用于特定的客户端。
3 添加用户并获取访问令牌在本节中,我们为应用程序创建和配置用户。 之前,我们在 1 和 2 节中配置了客户端及其作用域。 但是,除了客户端应用程序外,我们还需要用户对资源服务器提供的服务进行身份认证和访问。 我们配置了三个用于测试应用程序的用户( 图 14 )。 我将用户命名为 Mary,Bill 和 Rachel。
图 14
图 14 通过从左边的菜单导航到 Users 选项卡,您会发现您的应用程序的所有用户列表。在这里,您还可以通过单击 Users 表右上角的 Add user 来添加一个新用户。
当在 Add user 表单中添加一个新用户时,给它一个唯一的用户名,并选中表示已验证电子邮件的框 (图 15 )。另外,确保用户没有选择 Required User Actions。当用户有 Required User Actions 挂起时,您不能将其用于身份认证;因此,您无法获得该用户的访问令牌。
图 15
图 15 添加新用户时,给用户一个唯一的用户名,并确保该用户没有 Required User Actions
创建用户后,应该在 Users 列表中找到所有用户。图 16 显示了 Users 列表。
图 16
图 16 新创建的用户现在出现在 Users 列表中您可以从这里选择一个用户来编辑或删除。
当然,用户登录也需要密码。通常,他们会配置自己的密码,而管理员不应该知道他们的凭证。在我们的示例中,除了自己为这三个用户配置密码外,别无选择 ( 图 17 )。为了使我们的示例简单,我为所有用户配置了密码 “12345”。我还通过取消 Temporary 复选框来确保密码不是临时的。如果您将密码设置为临时的,Keycloak 会在用户第一次登录时自动为用户添加一个修改密码的操作。由于这个必需的操作,我们无法对用户进行身份认证。
图 17
图 17 您可以从列表中选择一个用户来更改或配置其凭证在保存更改之前,请记住确保将 Temporary 复选框设置为 OFF。如果凭证是临时的,您将无法预先对用户进行身份认证。
配置好用户后,您现在可以从使用 Keycloak 实现的授权服务器获得访问令牌。下一个代码片段将向您展示如何使用密码授权类型获取令牌,以保持示例的简单性。然而,正如您在 1 节中观察到的,Keycloak 也支持在《OAuth 2 是如何工作的?——(1)》中讨论的其他授权类型。图 18 是我们在那里讨论的密码授权类型。
要获取访问令牌,调用授权服务器的 /token 端点:
curl -XPOST "http://localhost:8080/auth/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=password" \
--data-urlencode "username=rachel" \
--data-urlencode "password=12345" \
--data-urlencode "scope=fitnessapp" \
--data-urlencode "client_id=fitnessapp"
图 18
图 18 当使用密码授权类型时,用户与客户端共享他们的凭证。客户端使用凭证从授权服务器获取访问令牌。有了令牌,客户端就可以访问由资源服务器公开的用户资源。
您在 HTTP 响应体中接收访问令牌。下面的代码片段显示了响应:
{
"access_token":"eyJhbGciOiJIUzI..."
"expires_in":6000
"refresh_expires_in":1800
"refresh_token":"eyJhbGciOiJIUz... "
"token_type":"bearer"
"not-before-policy":0
"session_state":"1f4ddae7-7fe0-407e-8314-a8e7fcd34d1b"
"scope":"fitnessapp"
}
注意
在 HTTP 响应中,我截短了 JWT 令牌,因为它们很长。
下一个代码片段表示 JWT 访问令牌的解码 JSON 体。看一下代码片段,您可以观察到令牌不包含使应用程序工作所需的所有详细信息。角色和用户名缺失。在 4 节中,您将学习如何将角色分配给用户,并自定义 JWT 以包含资源服务器需要的所有数据。
{
"exp": 1585392296
"iat": 1585386296
"jti": "01117f5c-360c-40fa-936b-763d446c7873"
"iss": "http://localhost:8080/auth/realms/master"
"sub": "c42b534f-7f08-4505-8958-59ea65fb3b47"
"typ": "Bearer"
"azp": "fitnessapp"
"session_state": "fce70fc0-e93c-42aa-8ebc-1aac9a0dba31"
"acr": "1"
"scope": "fitnessapp"
}
4 定义用户角色
在第 3 节中,我们设法获得了一个访问令牌。我们还添加了一个客户端 并配置用户以获得令牌。但是,令牌仍然没有我们的资源服务器应用授权规则所需的所有信息。要为我们的场景编写一个完整的应用程序,我们需要为用户添加角色。
向用户添加角色很简单。左侧菜单中的 Roles 选项卡允许您查找所有角色的列表并添加新角色,如图 19 所示。我创建了两个新角色,fitnessuser 和 fitnessadmin。
图 19
图 19 通过访问左侧菜单中的 Roles 选项卡,您可以找到所有已定义的角色,并可以创建新的角色。然后将它们分配给用户。
现在我们将这些角色分配给用户。我将角色 fitnessadmin 分配给我们的管理员 Mary,而 Bill 和 Rachel,他们是普通用户,担任角色 fitnessuser。图 20 向您展示了如何将角色附加到用户。
图 20
图 20 在所选用户的角色映射部分中,您分配角色。这些角色映射作为访问令牌中的用户权限显示,您可以使用它们来实现授权配置。
不幸的是,默认情况下,这些新的详细信息不会出现在访问令牌中。我们必须根据应用程序的需求定制令牌。我们通过配置在 2 节中创建并分配给令牌的客户端范围来定制令牌。我们需要向我们的令牌添加更多的三个信息:
- Roles — 用于根据场景在端点层应用部分授权规则
- Username — 在应用授权规则时过滤数据
- Audience claim (aud) — 资源服务器用来确认请求,您将在《动手操作:一个 OAuth 2 应用程序(3)》中了解到这一点。
下一个代码片段显示了在我们完成设置后添加到令牌的字段。然后,我们通过在客户端作用域上定义映射器来添加自定义声明,如图 21 所示。
{
// ...
"authorities": [
"fitnessuser"
]
"aud": "fitnessapp"
"user_name": "rachel"
// ...
}
图 21
图 21 我们为特定客户端作用域创建映射器来定制访问令牌。通过这种方式,我们提供了资源服务器授权请求所需的所有详细信息。
图 22 显示了如何创建映射器来将角色添加到令牌。我们在令牌中添加带有 authorities 键的角色,因为这是资源服务器所期望的方式。
图 22
图 22 为了在访问令牌中添加角色,我们定义了一个映射器。添加映射器时,需要为其提供一个名称。我们还指定要添加到令牌的详细信息和标识分配的详细信息的声明的名称。
使用类似于图 22 所示的方法,我们还可以定义一个映射器来将用户名添加到令牌。图 23 显示了如何为用户名创建映射器。
图 23
图 23 我们创建一个映射器将用户名添加到访问令牌当将用户名添加到访问令牌时,我们选择声明的名称 user_name,这是资源服务器期望在令牌中找到它的方式。
最后,我们需要指定受众。受众声明 ( aud ) 定义了访问令牌的预期接收者。我们为这个声明设置了一个值,并为资源服务器配置了相同的值,您将在《动手操作:一个 OAuth 2 应用程序(3)》中了解到这一点。图 24 向您展示了如何定义映射器,以便 Keycloak 可以将 aud 声明添加到 JWT。
图 24
图 24 表示映射器类型 Audience 的 aud 声明定义了访问令牌的接收者,在我们的例子中,是资源服务器。我们在资源服务器端配置相同的值,以便资源服务器接受令牌。
如果您再次获得访问令牌并对其进行解码,您应该在令牌的主体中找到 authorities、user_name 和 aud 声明。现在,我们可以使用这个 JWT 对资源服务器公开的端点进行身份认证和调用。现在我们已经在第 3 节中配置了一个完整的授权服务器,我们将为《动手操作:一个 OAuth 2 应用程序(1)》中介绍的场景实现资源服务器。下面的代码片段显示了令牌的主体:
{
"exp": 1585395055
"iat": 1585389055
"jti": "305a8f99-3a83-4c32-b625-5f8fc8c2722c"
"iss": "http://localhost:8080/auth/realms/master"
"aud": "fitnessapp"
"sub": "c42b534f-7f08-4505-8958-59ea65fb3b47"
"typ": "Bearer"
"azp": "fitnessapp"
"session_state": "f88a4f08-6cfa-42b6-9a8d-a2b3ed363bdd"
"acr": "1"
"scope": "fitnessapp"
"user_name": "rachel"
"authorities": [
"fitnessuser"
]
}
Keycloak 授权服务器我们已经基本配置好,下一篇我们就可以开始讨论资源服务器的实现。