Oauth2

使用 Keycloak 为 Spring Cloud Gateway 和 Spring Boot 微服务启用 OAuth2

本文将带你了解如何使用 Keycloak 为 Spring Cloud Gateway 和 Spring Boot 微服务启用 OAuth2。本文是 上一篇文章 的扩展,并分析了 Spring Security 项目中提供的一些最新功能。 我们的架构由两个 Spring Boot 微服务组成、一个基于 Spring Cloud Gateway 的 API 网关和一个 Keycloak 授权服务器。Spring Cloud Gateway 在此充当 OAuth2 客户端和 OAuth2 资源服务器。对于任何传入请求,它都会在将流量转发到下游服务之前验证 Access Token。对于任何未经验证的请求,它都会使用 Keycloak 初始化一个授权码授权的流程。我们的方案需要包括内部微服务之间的通信。它们都隐藏在 API 网关后面。caller 应用调用 callme 应用暴露的端点。通信中使用的 HTTP 客户端必须使用网关发送的 Access Token。 源码 本文中的代码托管在 Github,你可以克隆这个 Repository,进入到 oauth 目录,其中包含了两个 Spring Boot 微服务:callme 和 caller。当然,还有构建在 Spring Cloud Gateway 之上的 gateway 应用。之后,只需按照说明操作即可。 运行并配置 Keycloak 我们以 Docker 容器的形式运行 Keycloak。默认情况下,Keycloak 在 8080 端口上公开 API 和 Web 控制台。还需要通过环境变量设置管理员用户名和密码。下面是运行 Keycloak 容器的命令:

启用 Spring Cloud Gateway 的 OAuth2 支持,并将其与 Keycloak 集成

Spring Cloud Gateway 的 OAuth2 支持是微服务安全流程的关键部分。当然,使用 API 网关模式的主要原因是将服务隐藏起来,不对外部客户端可见。然而,在隐藏服务时,并没有对其进行安全保护。本文将带你了解如何使用 Spring Security 和 Keycloak 设置 Spring Cloud Gateway 的 OAuth2 功能。 源码 本文中的源码托管在 Github 上,你可以克隆 sample-spring-security-microservices 仓库,然后按照说明进行安装和部署即可。 在 Spring Cloud Gateway 中启用 OAuth2 要为 Spring Cloud Gateway 应用启用 OAuth2 支持,除了启用网关功能所必需的 spring-cloud-starter-gateway 依赖外,还需要添加 spring-boot-starter-oauth2-client 以启用 Spring Security 对 OAuth 2.0 授权框架和 OpenID Connect Core 1.0 的客户端支持,以及 spring-cloud-starter-security 来激活 TokenRelay Filter。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> 接下来为 OAuth2 客户端提供配置设置。因为要与 Keycloak 集成,所以应将 registrationId 的名称(spring.

Spring Cloud Gateway 和 Oauth2

1、概览 Spring Cloud Gateway 是一个响应式的轻量级网关,是 Spring Cloud 体系中一个比较重要的组件。本文将带你了解如何在其基础上快速实现 OAuth 2.0 认证、授权。 2、OAuth 2.0 快速回顾 OAuth 2.0 标准是一个成熟的标准,在互联网上广泛使用,是用户和应用安全访问资源的一种安全机制。 其中涉及的关键术语如下: Resource(资源):只有经过授权的客户端才能检索的任何类型的信息。 Client(客户端):消费资源的应用,通常通过 REST API 消费资源。 Resource Server(资源服务器):负责向授权客户端提供资源的服务。 Resource Owner(资源所有者):实体(人或应用),拥有资源,并最终负责向客户端授予对该资源的访问权限。 Token(令牌):客户端获取的一段信息,并作为请求的一部分发送给资源服务器以进行身份验证。 Identity Provider(身份提供商,即 IDP):验证用户凭证并向客户端发放 Access Token。 Authentication Flow(认证模式/流程):客户端获得有效 Token 必须经过的一系列步骤。 你可以通过 Auth0 的相关文档了解更多详细内容。 3、OAuth 2.0 模式 Spring Cloud Gateway 主要用于以下用途之一: OAuth Client(客户端) OAuth Resource Server(资源服务器) 我们来逐个了解。 3.1、Spring Cloud Gateway 作为 OAuth 2.0 客户端 在这种情况下,任何未经身份认证的传入请求都将触发授权码流程。一旦网关获取到 Token,它将在向后端服务发起请求时使用该 Token。 在实际应用中,一个很好的例子是聚合了 “社交应用” 的应用:对于每个支持的社交应用,网关将充当 OAuth 2.0 客户端。 因此,前端(通常是使用 Angular、React 或类似 UI 框架构建的 SPA 应用)可以代表终端户无缝访问这些应用上的数据。更重要的是:用户无需暴露自己的凭证。

Feign 客户端设置 Oauth2 Token

1、概览 OpenFeign 是一个可以在 Spring Boot 中使用的声明式 REST 客户端。 假如想通过 OpenFeign 来调用使用 OAuth2 的 REST API,那么就需要给 OpenFeign 设置 Access Token。 本文将会带你了解如何为 OpenFeign 客户端添加 OAuth2 支持。 2、服务之间的认证 服务之间的认证 API 安全中的一个热门话题。我们可以使用 mTLS 或 JWT 为 REST API 提供认证机制。不过,OAuth2 协议是保护 API 的事实解决方案。假设我们想使用另一个服务(客户端)调用一个受保护的服务(服务器)。在这种情况下,使用 “客户端凭证(client credential)” 授权方式。这种授权方式通常用于在两个没有终端用户的 API 或系统之间进行身份认证。 下图显示了这种授权模式中的的主要角色关系: 在客户端凭证模式中,客户端通过 Token Endpoint 从授权服务器(Authorization Server)获取 Access Token。然后,客户端使用 Access Token 访问源服务器(Resource Server)上受资保护的资源。资源服务器会验证 Access Token,如果有效,则为请求提供服务。 2.1、授权服务器 创建一个授权服务器来发放 Access Token。为了方便,我们在 Spring Boot 中使用嵌入式 Keycloak。假设我们使用 GitHub 上的 授权服务器 项目。 首先,在嵌入式 Keycloak 服务器的 realm 管理中定义 Payment-app 客户端:

Spring Security OAuth 2 教程 - 10:使用“客户端凭证模式”进行服务间的通信

在本文中,我们将学习如何使用 “客户端凭证模式”(Client Credentials Flow)实现服务间的通信。我们将创建 archival-service,在其中通过定时任务使用 “客户端凭证模式” 来调用 messages-service API 以归档消息。 我们还会在 archival-service 中实现 POST /api/messages/archive API 端点,只有拥有 ROLE_ADMIN 角色的用户才能调用。 有鉴于此,archival-service 既是资源服务器(Resource Server),也是客户端。 资源服务器 - 暴露 POST /api/messages/archive API 端点,该端点将由 messages-webapp 调用。 客户端 - 调用 messages-service API 来归档消息。 你可以从 Github 仓库 获取到当前项目的完整源码。 在 Keycloak 中启用客户端凭证模式,创建 archival-service 客户端 创建一个名为 archival-service 的新客户端: General Settings: Client type:OpenID Connect Client ID:archival-service Capability config: Client authentication:On Authorization:Off Authentication flow:选中 Service accounts roles,取消选中其余复选框 Login settings: Root URL: http://localhost:8282 Home URL: http://localhost:8282 使用上述配置创建客户端后,你将进入新创建的客户端 “Settings” 页面。

Spring Security OAuth 2 教程 - 9:客户端调用资源服务器 API

在前面的文章中,我们创建了 messages-webapp 和 messages-service,并使用 Postman 调用了 API 端点。在本文中,我们将学习如何从客户端应用 messages-webapp 调用受保护的 messages-service API 端点。 你可以从 Github 仓库 获取到完整的源码。 展示消息列表 由于 messages-service 中的 GET /api/messages API 端点是可公开访问的,因此我们可以从 messages-webapp 调用它,而无需任何身份认证。 RestTemplate 和 RestClient 我们使用传统的 RestTemplate 来调用 messages-service 中的 API 端点。但在 Spring Boot 3.2.0 后,建议改用 RestClient。 在 messages-webapp 中,创建 AppConfig 类,如下: package com.sivalabs.messages.config; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class AppConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } } 我们注册了一个 RestTemplate Bean,以便将其注入到其他组件中。

Spring Security OAuth 2 教程 - 8:资源服务器

在 上一篇文章 中,我们创建了 messages-webapp,并使用 “授权码模式” 通过 Spring Security OAuth 2.0 对其进行了访问控制。在本文中,我们将创建 messages-service(Spring Boot 资源服务器),并使用 Spring Security OAuth 2.0 进行访问控制。 你可以在 Github 仓库 找到该项目完整的源码。 创建 messages-service 点击此 链接 可使用 Spring Initializr 生成 messages-service。我们选择了 Web、Validation、Security 和 OAuth2 Resource Server Starter。应用生成后,在 IDE 打开它。 配置 OAuth 2.0 资源服务器属性 messages-service 是 bearer-only 类型的资源服务器。这意味着如果有人使用有效的 access_token 作为 Authorization 头发送请求到受保护的 API 端点,该服务将返回响应。否则,它将只会返回 401 或 403 的 HTTP 状态码,而不会启动 OAuth 2.0 的授权流程。 bearer-only 类型的资源服务器无需向授权服务器(Keycloak)注册。我们只需在 application.properties 文件中配置 issuer-uri 如下: spring.application.name=messages-service server.

Spring Security OAuth 2 教程 - 7:Spring MVC 客户端应用

在本文中,我们将创建一个名为 messages-webapp 的 Spring MVC + Thymeleaf Web 应用,并使用 Keycloak 进行访问控制,使用 Spring Security OAuth 2.0 进行认证。 你可以在 Github 上找到该项目的完整源码。 使用 Docker Compose 安装 Keycloak 在上一篇文章中,我们已经了解了如何使用 Docker Compose 安装 Keycloak。 创建 docker-compose.yml 文件,内容如下: version: '3.8' name: spring-security-oauth2-microservices-demo services: keycloak: image: quay.io/keycloak/keycloak:22.0.3 command: ['start-dev'] container_name: keycloak hostname: keycloak environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin1234 ports: - "9191:8080" 运行以下命令启动 Keycloak 实例: $ docker compose up -d 现在,你可以访问 Keycloak 管理控制台 http://localhost:9191/,并使用 admin/admin1234 登录。 创建 Keycloak Realm、客户端和用户 在前面的文章中,我们已经学习了如何创建 Realm、客户端和用户。请按照 前文 中提到的步骤创建新的 Realm、客户端和用户,只需更改 “Valid redirect URIs”。

Spring Security OAuth 2 教程 - 6:微服务项目设置

在之前的文章中,我们学习了使用 Web 浏览器、cURL 和 Postman 进行各种 OAuth 2.0 / OpenID Connect Flow。现在是时候学以致用了。 在使用像 Spring Security 这样的安全框架实现基于 OAuth 2.0 / OpenID Connect 的 Security 时,许多活动都是由框架在幕后执行的。了解框架内部发生的情况非常重要,这样我们才能有效地使用框架。 在本文中,我将简要介绍我们将在 Spring Security OAuth 2 教程系列中使用的基于微服务的示例项目。 示例 Spring Boot 微服务项目 我们将构建一个包含以下组件的微服务示例项目: Authorization Server - 这是 OAuth 2.0 授权服务器,将向客户端发放访问令牌。我们将使用 Keycloak 作为授权服务器。 messages-webapp - 终端用户将使用的基于 Spring MVC 的客户端应用。 messages-service - Spring Boot REST API(资源服务器)用于管理用户数据(消息),并将由 messages-webapp 应用使用。 archival-service - Spring Boot 应用可使用定时任务定期处理消息存档。它还提供了一个 API 端点来触发存档过程。该服务将同时扮演资源服务器和客户端的角色。 让我们来了解一下这些组件各自提供的功能。 messages-webapp(客户端) 这是一个使用 Spring MVC 和 Thymeleaf 渲染用户界面的 Spring Boot Web 应用。该应用将受到授权服务器(Keycloak)的保护,并将使用 “授权码模式” 来认证用户身份并获取访问令牌。

Spring Security OAuth 2 教程 - 5:隐式模式 & 资源所有者密码模式

在 “Spring Security OAuth 2 教程 - 4:PKCE 授权码模式” 中,我们学习了如何通过 PKCE 授权码模式获取访问令牌(access_token)。在本文中,我们将了解如何使用隐式模式(Implicit Flow)和资源所有者密码凭证模式(Resource Owner Password Credentials Flow)。 注意 隐式模式和资源所有者密码凭证模式已被淘汰,没有特别理由不应该再使用。 隐式模式 隐式模式(Implicit Flow)是授权码模式(Authorization Code Flow)的一种简化版本,你可以直接通过授权端点(authorization_endpoint)获取访问令牌(access_token)。 如果你一直关注本系列,就会知道如何创建一个启用了特定 “Authentication flow” 的客户端。这里,我们直接为 messages-webapp 客户端启用隐式模式。 要了解如何创建新客户端,请参阅 创建客户端。 在浏览器窗口中打开以下 URL: http://localhost:9191/realms/sivalabs/protocol/openid-connect/auth? response_type=id_token%20token &client_id=messages-webapp &redirect_uri=http://localhost:8080/callback &scope=openid%20profile &state=randomstring &nonce=another_randomstring 然后,你将被重定向到 Keycloak 的登录页面。 使用凭证 siva/siva1234 登录。 然后,你将被重定向到包含 access_token 和 id_token 查询参数的重定向 URI。 http://localhost:8080/callback# state=randomstring &session_state=51692fdb-8b72-45b7-a341-fa73e97b5139 &id_token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeVVPTDg4LVBGM3BYQzFpN3BIeGdFZTJwaWZJY3RyTXJiNklHOElmRTlVIn0.eyJleHAiOjE2OTU2MTUzOTQsImlhdCI6MTY5NTYxNDQ5NCwiYXV0aF90aW1lIjoxNjk1NjE0NDk0LCJqdGkiOiI2MjFmNTJmMC0wMDBmLTQ1ZmUtYWYzOC1iY2YzZWM2ZDk1MTEiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkxOTEvcmVhbG1zL3NpdmFsYWJzIiwiYXVkIjoibWVzc2FnZXMtd2ViYXBwIiwic3ViIjoiY2ExYTJmMzQtMTYxNC00NWRkLTg2YzEtNWVhZmZmMDg1ZDhhIiwidHlwIjoiSUQiLCJhenAiOiJtZXNzYWdlcy13ZWJhcHAiLCJub25jZSI6ImFub3RoZXJfcmFuZG9tc3RyaW5nIiwic2Vzc2lvbl9zdGF0ZSI6IjUxNjkyZmRiLThiNzItNDViNy1hMzQxLWZhNzNlOTdiNTEzOSIsImF0X2hhc2giOiJiamR6MC1NeWltQ0xrSzdqaWRRbHp3IiwiYWNyIjoiMSIsInNfaGFzaCI6IlJtVE5Ld0lYaTNXRFhzRFlObTQtUHciLCJzaWQiOiI1MTY5MmZkYi04YjcyLTQ1YjctYTM0MS1mYTczZTk3YjUxMzkiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IlNpdmEgS2F0YW1yZWRkeSIsInByZWZlcnJlZF91c2VybmFtZSI6InNpdmEiLCJnaXZlbl9uYW1lIjoiU2l2YSIsImZhbWlseV9uYW1lIjoiS2F0YW1yZWRkeSIsImVtYWlsIjoic2l2YUBnbWFpbC5jb20ifQ.TIcmVBti96HuZvrYe_14mVJlfopXI2PhdMdWBtPPASpJc-DKrL9argy08sYZKqJTTcmWwnIwKK2o1vddVxA4zUP2tnqqg6ymz1trN3J8r4h-WSvIp907vnS0R7iHei56L6MQX2DZLJ8pOdSmti8wg_9fu4gQJBE2sHRTlrlOP39dh8yohMGidM-Z5iFbLCIzOQXA6B6ewMZll5iwL3ssJ716Ve9cO4qHGCneRGpb3mO7jclY87YSGM-wqr6ur00ylQ_BCGyCdl-f-xskSeDX09iQKFSTX_acMxB7FNi21BL7dMx8_22XPFOwNkX8ha8Vb7eTRMYEMyB776i33FLu0A &access_token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeVVPTDg4LVBGM3BYQzFpN3BIeGdFZTJwaWZJY3RyTXJiNklHOElmRTlVIn0.eyJleHAiOjE2OTU2MTUzOTQsImlhdCI6MTY5NTYxNDQ5NCwiYXV0aF90aW1lIjoxNjk1NjE0NDk0LCJqdGkiOiI1N2NiYmRkMC0wNGFmLTRlMDctYWZlNC02ZmQ5MmY0YjA2MzAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkxOTEvcmVhbG1zL3NpdmFsYWJzIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImNhMWEyZjM0LTE2MTQtNDVkZC04NmMxLTVlYWZmZjA4NWQ4YSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im1lc3NhZ2VzLXdlYmFwcCIsIm5vbmNlIjoiYW5vdGhlcl9yYW5kb21zdHJpbmciLCJzZXNzaW9uX3N0YXRlIjoiNTE2OTJmZGItOGI3Mi00NWI3LWEzNDEtZmE3M2U5N2I1MTM5IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtc2l2YWxhYnMiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsInNpZCI6IjUxNjkyZmRiLThiNzItNDViNy1hMzQxLWZhNzNlOTdiNTEzOSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiU2l2YSBLYXRhbXJlZGR5IiwicHJlZmVycmVkX3VzZXJuYW1lIjoic2l2YSIsImdpdmVuX25hbWUiOiJTaXZhIiwiZmFtaWx5X25hbWUiOiJLYXRhbXJlZGR5IiwiZW1haWwiOiJzaXZhQGdtYWlsLmNvbSJ9.XsYc69HnM9VaJZFF568nRiZhh8RYEw6Hq2WGnJ4jr3tmZvgMF0QK2RtlBT9BuX4A11XHjyNZqGYNf55x0k4bPXjhPzWI-lC0shhsKrXGYrnhVcComXxMbO_38ypRY_EMeBWRTXu0bvcKInYMjVoItfoLheH-kbcziK6O16yFGftOG-YYw0uVzs_DrOkjQjs1BS2L56yXcRgN72EBXMT-Cv6OLMTSj6WXjfg1nmRl0NRJdeZv0iafSolmqSpJeqXwPzM2hgZ2hPzaq90qipndQrZ05xesMtzXMNlev0ozYPN7xKSa7arHMYky8y4OMpCQDJzkSwekjUEQUSU9Sqg_VA &token_type=Bearer &expires_in=900 如你所见,access_token 和 id_token 是通过浏览器 URL 前端返回的,这并不安全。 资源所有者密码凭证模式 在 “资源所有者密码凭证模式” 中,我们将使用资源所有者(终端用户)凭证获取 access_token 和 id_token。