使用 Spring Boot 和 Keycloak 的 OAuth2 快速指南

1. 概述

本教程是关于使用 Spring Boot 和 Keycloak 通过 OAuth2 配置后端的。

我们将使用 Keycloak 作为 OpenID 提供程序。我们可以将其视为负责身份验证和用户数据(角色、配置文件、联系信息等)的用户服务。它是最完整的 OpenID Connect (OIDC) 实现之一,具有以下功能:

  • 单点登录 (SSO) 和单点注销(Back-Channel Logout)

  • 身份代理、社交登录和用户联合

  • 用于服务器管理和用户帐户管理的用户界面

  • 可通过编程方式控制一切的管理员 REST API

在 Spring Security 中查看 OAuth2 的配置选项后,我们将配置两个不同的 Spring Boot 应用程序:

  • 使用 oauth2Login 的有状态客户端

  • 无状态的 oauth2 ResourceServer

2. 使用 Docker 的 Keycloak 快速入门

在本节中,我们将启动一个具有预配置 Realm 的 Keycloak 服务器。我们将在第 6 节中了解如何创建这样的 Realm。

2.1. Docker Compose 文件

在开发人员的桌面上对授权服务器进行沙盒化的最简单方法是拉取 Keycloak Docker 镜像。要配置它,我们将使用 Docker compose 文件:


services:postgres:image: postgres:16.2ports:- 5432:5432volumes:- ./postgres_data:/var/lib/postgresql/data- /etc/localtime:/etc/localtime:roenvironment:POSTGRES_DB: ${POSTGRES_DB}POSTGRES_USER: ${POSTGRES_USER}POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}networks:- keycloak_networkkeycloak:image: quay.io/keycloak/keycloak:26.1.2command: startenvironment:KC_HOSTNAME: 192.168.1.212KC_HOSTNAME_PORT: 8080KC_HOSTNAME_STRICT_BACKCHANNEL: falseKC_HTTP_ENABLED: trueKC_HOSTNAME_STRICT_HTTPS: falseKC_HEALTH_ENABLED: trueKEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}KC_DB: postgresKC_DB_URL: jdbc:postgresql://postgres/${POSTGRES_DB}KC_DB_USERNAME: ${POSTGRES_USER}KC_DB_PASSWORD: ${POSTGRES_PASSWORD}TZ: Asia/Shanghaivolumes:- ./keycloak/:/opt/keycloak/data/import/- /etc/localtime:/etc/localtime:roports:- 8080:8080restart: alwaysdepends_on:- postgresnetworks:- keycloak_networkvolumes:postgres_data:driver: localnetworks:keycloak_network:driver: bridge
POSTGRES_DB=keycloak_db
POSTGRES_USER=keycloak_db_user
POSTGRES_PASSWORD=keycloak_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=password

2.2. 使用 Companion Project Realm

配套项目包含一个 JSON 文件,其中包含我们需要的一些 Keycloak 对象的定义:

  • realm:cemx

  • client:cemc-client,需要携带 secret 凭证,开启Client authentication

  • 一个映射器,用于将realm role 添加到颁发给 cemx-keycloak-client 客户端的 Access Token和 ID Token(默认情况下,只有访问令牌包含领域角色)

  • 在领域级别定义的 ADMIN 角色(Keycloak 还支持在客户端级别定义角色)

  • 两个用户:davy(具有 ADMIN 角色)和 test(没有 ADMIN 角色)。

compose 文件中的 —import-realm 参数指示 Keycloak 从其 data/import/ 文件夹中的 JSON 文件加载对象。

给定 compose 文件中定义的卷,我们应该keycloak/cemx-keycloak-realm.json 文件从配套项目复制到相对于 compose 文件的 ./keycloak/ 文件夹

2.3. 启动 Docker 容器

为了简化初始配置,上面的 Docker compose 文件使用了一个 KEYCLOAK_ADMIN_PASSWORD 环境变量,我们应该在启动容器之前设置该变量:

export KEYCLOAK_ADMIN_PASSWORD=admin
docker compose up -d

运行这些命令后,Keycloak 将启动。一旦我们看到一行包含 Keycloak 26.1.2 […] started,我们就知道启动完成。

我们现在可以通过浏览到 http://l192.168.1.212:8080,使用 admin/admin 作为凭据来使用 Keycloak 管理控制台。

3. Spring Security for OAuth2

3.1. OAuth2 参与者

让我们首先了解 OAuth2 的三个主要参与者。

Authorization server:负责资源所有者的身份验证并向客户端颁发令牌** – 在本教程中,我们将使用 Keycloak 来实现此目的

客户端驱动流程:从授权服务器获取令牌、存储令牌,并使用有效令牌授权对资源服务器的请求。当资源所有者是用户时,客户端使用授权代码流程(或者,当用户设备的输入功能有限时,使用设备流程)针对授权服务器登录,并获取令牌以代表他们向资源服务器发送请求。

在本教程中,我们的客户端是具有 oauth2Login 和 Postman 的 Spring 应用程序。

Resource server:提供对 REST 资源的安全访问**。它们检查令牌的有效性(issuer、expiration、audience等)并控制客户端访问(client scopes, resource owner roles, 资源所有者与访问的资源之间的关系等)。我们将 REST API 配置为资源服务器。

从上面可以看出,选择从授权服务器获取令牌的流是 OAuth2 客户端的责任。因此,用户登录和注销从来都不是资源服务器的问题

3.2. oauth2登录

Spring Security oauth2Login 配置授权代码和刷新令牌流。它还配置了一个授权的客户端存储库来存储令牌(默认在会话中),并配置了一个授权的客户端管理器供我们访问它 - 在需要时刷新令牌,然后再返回令牌。

使用 oauth2Login 对 Spring 客户端的请求是通过会话 cookie 授权的。这就是为什么应该始终在具有oauth2Login的Security(Web)FilterChainbean 中启用对 CSRF 攻击的保护

成功授权请求后,安全上下文中的 Authentication 类型为 OAuth2AuthenticationToken。

如果使用 OpenID 自动配置提供程序,则 OAuth2AuthenticationToken 主体是从 ID 令牌构建的 OidcUser*,否则,需要对 userinfo 端点进行额外调用才能将 OAuth2User 设置为主体。

Spring Security 权限是使用GrantedAuthoritiesMapper或自定义OAuth2UserService进行映射的。

3.3. oauth2ResouceServer 服务器

Spring Security oauth2ResouceServer配置 Bearer token 安全性。它提供了introspection(不透明令牌)和 JWT decoding。

对于资源服务器,用户状态由令牌声明保存,并且可以禁用会话。这带来了两大好处:

  • 可以禁用 CSRF 保护 – CSRF 攻击涉及会话,此处未使用

  • 资源服务器易于扩展 – 无论请求路由到哪个实例,客户端和资源所有者的状态都会随声明一起提供

成功授权请求后,安全上下文中的 Authentication 类型可以使用身份验证转换器进行自定义。但是,默认身份验证类型、如何从令牌转换以及如何解析它包含的权限的详细信息取决于资源服务器类型:

  • 使用 JWT decoder时,默认身份验证类型为 JwtAuthenticationToken,并且使用访问令牌声明将颁发机构映射到 JWT 身份验证转换器
  • 使用 access tokens introspection时,默认身份验证类型为 BearerTokenAuthentication,并且使用内省端点响应通过自定义内省器

令牌类型适用性:

  • JWT decoder:
    适用于 JWT 类型的访问令牌。JWT 具有自包含性,使得资源服务器可以独立地验证和解析令牌,无需频繁与授权服务器通信。
  • access tokens introspection:
    适用于各种类型的访问令牌,包括Bearer Token(即令牌本身不包含可直接解析的信息)。Bearer Token通常由授权服务器生成并管理,其内容对资源服务器是不透明的,因此需要通过内省机制向授权服务器查询令牌的有效性和相关信息。

3.4. 在 oauth2Login 和 oauth2ResourceServer 之间进行选择

从上面我们可以看出 oauth2Loginoauth2ResourceServer 的用途是不同的。Spring Security 为每个提供了不同的 Spring Boot 启动器,因为两者不应该位于同一个Security(Web)FilterChain bean 中

3.4.1 两者的区别
  • 授权基础

    • oauth2Login:基于会话(Session)进行授权。当用户登录成功后,服务器会创建一个会话,在会话期间跟踪用户的状态和权限信息。例如,用户登录一个电商网站,登录成功后服务器创建会话,在用户浏览商品、添加到购物车等操作时,服务器通过会话识别用户身份。

    • oauth2ResourceServer:基于 Bearer 令牌进行授权。客户端在请求资源时,需要在请求头中携带有效的 Bearer 令牌,资源服务器通过验证该令牌来确定请求的合法性。比如,一个移动应用请求获取用户的订单信息,需要在请求头中添加Authorization: Bearer < token >。

  • CSRF 保护

    • oauth2Login:由于基于会话,存在 CSRF(跨站请求伪造)攻击的风险,所以需要进行 CSRF 保护。CSRF 攻击是指攻击者通过诱导用户在已登录的网站上执行恶意操作,利用用户的会话身份进行非法请求。例如,用户在已登录银行网站的情况下,访问了恶意网站,恶意网站可能会利用用户在银行网站的会话发起转账请求。

    • oauth2ResourceServer:由于采用无状态的 Bearer 令牌机制,不存在会话的概念,所以不需要 CSRF 保护。每个请求都携带独立的令牌,资源服务器只验证令牌的有效性,而不依赖会话状态。

  • 认证和权限映射差异

    • oauth2Login:认证类型通常与用户登录过程相关,权限映射可能基于用户在身份提供者处的角色和权限信息。例如,用户使用 Google 登录应用,应用会根据 Google 返回的用户信息和角色来分配相应的权限。

    • 使用 JWT 解码器的 oauth2ResourceServer:默认的认证类型是JwtAuthenticationToken,权限通过 JWT 认证转换器利用访问令牌声明进行映射。JWT 令牌本身包含了用户的权限信息,资源服务器通过解码 JWT 令牌来获取这些信息。

    • 使用内省机制的 oauth2ResourceServer:默认的认证类型是BearerTokenAuthentication,权限通过使用内省端点响应的自定义内省器来解析。资源服务器将令牌发送到授权服务器的内省端点,授权服务器返回令牌的有效性和权限信息。

3.4.2 REST 端点配置倾向

由于无状态应用的可扩展性优势,通常更倾向于使用oauth2ResourceServer来配置 REST 端点。无状态应用的每个请求都是独立的,不依赖于服务器端的会话状态,因此可以轻松地进行水平扩展,通过增加服务器实例来处理更多的请求。

但使用oauth2ResourceServer要求向 REST API 发送(或路由)请求的组件能够完成一系列操作,包括从授权服务器获取令牌、将令牌存储在某种状态(如会话或本地存储)中、在令牌过期时刷新令牌,并在请求时使用访问令牌进行授权。

3.4.3 oauth2Login 的主要用例及扩展问题
  • 主要用例:oauth2Login主要用于服务器端渲染(SSR)的用户界面和将 Spring Cloud Gateway 用作 OAuth2 后端前端(BFF)。在服务器端渲染的应用中,用户通过登录页面进行身份验证,服务器生成包含用户信息的会话,然后渲染页面并返回给客户端。

  • 扩展问题:由于oauth2Login基于会话,是有状态的,在进行水平扩展以实现高可用性或负载均衡时,需要使用 Spring Session 或智能代理等技术来管理会话状态,确保用户在不同服务器实例之间的会话一致性。例如,使用 Spring Session 可以将会话信息存储在 Redis 等分布式缓存中,使得不同的服务器实例都可以访问和管理会话。

3.5. 组合异构请求授权机制

为了授权来自不同异构消费者的请求,我们可能需要使用 oauth2Loginoauth2ResourceServerx509formLoginBasic auth 等配置多个应用程序。例如,在 OAuth2 BFF 上,前端请求使用 oauth2Login 授权,而 actuator 端点使用 oauth2ResourceServerBasic 进行授权。

在这种情况下,每个请求授权机制都应该使用不同的Security(Web)FilterChain bean,所有 bean 都用不同的 @Order 装饰,除了最后一个 bean 之外,所有 bean 都按顺序使用 securityMatcher 定义它应该应用于哪个请求。如果没有 securityMatcher,则过滤器链将用作到目前为止尚未匹配的所有请求的默认值。

4. Thymeleaf 与登录

我们使用 Spring Boot 和 Keycloak 的 OAuth2 的第一个用例是 Thymeleaf 应用程序,它使用 OpenID Provider 对用户进行身份验证。它很简单,但是,演示了在有状态应用程序中使用 Keycloak 的基于角色的访问控制 (RBAC)。

值得注意的是:

  • 在具有单页或移动应用程序的系统中,我们将使用具有类似 OAuth2 客户端配置的 OAuth2 BFF

  • 我们的 Thymeleaf 应用程序是一个 OAuth2 客户端,因为它使用 oauth2Login 和 ID 令牌来构建用户身份验证,但它不使用访问令牌(它不会向资源服务器发送请求)。如前所述,浏览器和我们的 Spring 后端之间的请求是通过会话 cookie 授权的。

  • 要水平扩展,我们需要跨实例共享会话(Spring Session)或智能代理,将来自同一用户代理的所有请求根到同一实例。

4.1. 依赖项

使用 OAuth2 启用用户登录的最重要依赖项是 spring-boot-starter-oauth2-client。当然,当我们创建一个渲染 Thymeleaf 模板的 servlet 应用程序时,我们还需要spring-boot-starter-webspring-boot-starter-thymeleaf

让我们将它们添加到pom.xml中:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

如果我们想模拟身份验证以测试 Spring OAuth 2 应用程序中的访问控制,我们还需要具有测试范围的spring-security-test

4.2. 提供者和注册配置

由于 Keycloak 在 ${issuer-uri}/.well-known/openid-configuration 中公开了其 OpenID 配置,并且鉴于 Spring Security 可以从其 OpenId 配置中自动配置提供程序,因此定义其issuer-uri就足够了:

spring.security.oauth2.client.provider.cemx-keycloak.issuer-uri=http://192.168.1.121:8080/realms/cemx

现在,让我们使用上面的提供程序配置客户端注册

spring.security.oauth2.client.registration.keycloak.provider=cemx-keycloak
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.client-id=cemc-client
spring.security.oauth2.client.registration.keycloak.client-secret=**********
spring.security.oauth2.client.registration.keycloak.scope=openid

我们在 client-id 中指定的值与 Keycloak 管理控制台中client一致,从 Credentials (凭据) 选项卡中获取 client-secret 的值。

注意上述“keycloak”这个字眼,后面在配置Keycloak的 Valid redirect URIs http://192.168.1.128:8081/login/oauth2/code/keycloak ,要保持一致

4.3. 将 Keycloak Realm 角色映射到 Spring 安全权限

有多种方法可以使用 oauth2Login 在过滤器链中映射权限,但最简单的可能是 暴露 一个 GrantedAuthoritiesMapper bean,本教程采用该方法。

让我们首先定义一个 bean,负责从 Keycloak 声明集中提取权限,它可以是 ID 或访问令牌有效负载,以及 userinfo 或内省响应正文:

interface AuthoritiesConverter extends Converter<Map<String, Object>, Collection<GrantedAuthority>> {
}@SuppressWarnings("unchecked")
@Bean
AuthoritiesConverter realmRolesAuthoritiesConverter() {return claims -> {final var realmAccess = Optional.ofNullable((Map<String, Object>) claims.get("realm_access"));final var roles = realmAccess.flatMap(map -> Optional.ofNullable((List<String>) map.get("roles")));return roles.stream().flatMap(Collection::stream).map(SimpleGrantedAuthority::new).map(GrantedAuthority.class::cast).toList();};
}

由于 JVM 中的泛型类型擦除,并且由于应用程序上下文中可能有许多具有不同输入和输出的 Converter bean,因此当 Bean 工厂搜索 Converter<Map<String、Object>、Collection> Bean 时,此 AuthoritiesConverter 接口的的约束和提示是非常有用的。

当我们使用其 issuer-uri 自动配置 OIDC 提供程序时,我们在 GrantedAuthoritiesMapper 中获得的输入是 OidcUserAuthority 实例:

@Bean
GrantedAuthoritiesMapper authenticationConverter(Converter<Map<String, Object>, Collection<GrantedAuthority>> realmRolesAuthoritiesConverter) {return (authorities) -> authorities.stream().filter(authority -> authority instanceof OidcUserAuthority).map(OidcUserAuthority.class::cast).map(OidcUserAuthority::getIdToken).map(OidcIdToken::getClaims).map(realmRolesAuthoritiesConverter::convert).filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toSet());
}

4.4. 将SecurityFilterChain Bean 放在一起

以下是我们将使用的完整 SecurityFilterChain

@Bean
SecurityFilterChain clientSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {http.oauth2Login(Customizer.withDefaults());http.logout((logout) -> {final var logoutSuccessHandler =new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}/");logout.logoutSuccessHandler(logoutSuccessHandler);});http.authorizeHttpRequests(requests -> {requests.requestMatchers("/", "/favicon.ico").permitAll();requests.requestMatchers("/admin").hasAuthority("ADMIN");requests.anyRequest().denyAll();});return http.build();
}

说明:

oauth2Login() 方法将 OAuth2LoginAuthenticationFilter 添加到过滤器链中。此过滤器拦截请求并应用所需的逻辑来处理授权代码和刷新令牌流。它还在 session 中存储令牌。

要了解 OAuth2 注销的工作原理,我们应该记住,用户至少有两个独立的会话:一个在授权服务器上(在我们的例子中是 Keycloak),另一个在具有 oauth2Login 的每个客户端上。要完全注销,必须终止所有会话

OpenID 标准定义了实现此目的的不同方法,但我们将重点介绍使用 OidcClientInitiatedLogoutSuccessHandler配置的 OpenID Connect RP-Initiated Logout。

在此流程中,用户代理(用户的浏览器)首先从依赖方(使用 oaut2Login 的 Spring 应用程序)注销以关闭其会话。RP 通过重定向到 OpenID 提供程序 (OP) 作为响应,其中包含链接到要关闭的会话的 ID 令牌和注销后 URL(在我们的例子中为 Thymeleaf 索引)。然后,OP 关闭其会话并将用户代理重定向到提供的 URL。

总而言之,我们定义了访问控制规则:

  • 要使用户从索引登录,它必须可供匿名请求访问。

  • /admin 路径只能由被授予ADMIN 权限的经过身份验证的用户访问。

4.5. Thymeleaf 网页

我们将 Thymeleaf 用于我们的两个网页:

  • index.html 根据用户的状态显示登录注销按钮。它还包含一个用于导航到 /admin 的按钮,为了获得良好的用户体验,我们只向具有 ADMIN 权限的经过身份验证的用户显示。

  • admin.html 仅包含静态内容。

我们的 index.html 具有条件逻辑,能够使用用户会话中的值:

<div class="container"><h1 class="form-signin-heading">Cemx: Keycloak &amp; Spring Boot</h1><p>Welcome to a Thymeleaf UI served by Spring Boot and secured using Keycloak!</p><div th:if="${!isAuthenticated}"><a href="/oauth2/authorization/keycloak"><button type="button"class="btn btn-lg btn-primary btn-block">Login</button></a></div><div th:if="${isAuthenticated}"><p>Hi <span th:utext="${name}">..!..</span>!</p><a href="/logout"><button type="button" class="btn btn-lg btn-primary">Logout</button></a><a th:if="${isAdmin}" href="/admin"><button type="button" class="btn btn-lg btn-primary">Browse to Admin userspage</button></a></div>
</div>

4.6. 控制器

UiController 构建索引页模型(name,以及 isAuthenticatedisAdmin 标志),并解析 Thymeleaf 模板:

@Controller
public class UiController {@GetMapping("/")public String getIndex(Model model, Authentication auth) {model.addAttribute("name",auth instanceof OAuth2AuthenticationToken oauth && oauth.getPrincipal() instanceof OidcUser oidc ? oidc.getPreferredUsername() : "");model.addAttribute("isAuthenticated", auth != null && auth.isAuthenticated());model.addAttribute("isAdmin", auth != null && auth.getAuthorities().stream().anyMatch(authority -> Objects.equals("ADMIN", authority.getAuthority())));return "index.html";}@GetMapping("/admin")public String getAdmin(Model model, Authentication auth) {return "admin.html";}
}

4.7. 使用 oauth2Login 尝试 Thymeleaf 应用程序

现在,我们可以使用我们最喜欢的 IDE 启动应用程序。或者,从 maven 父上下文中,我们将运行:

sh ./mvnw -pl spring-boot-mvc-client spring-boot:run

通过将浏览器指向 http://192.168.1.128:8081/,我们应该会看到一个带有登录按钮的页面。

导航到为 ADMIN 用户保留的页面的按钮只有在以 davy 身份(而不是 test)登录时才可见。

RP 发起的注销后的下一次登录尝试中,我们应该必须输入凭证。

如果我们从 Java conf 中删除 logout 部分,则从 Spring 应用程序注销时 Keycloak 会话不会结束,新的登录尝试将静默完成。Keycloak 不会显示登录表单,因为从它的角度来看,我们仍然处于登录状态。

5. 带有 JWT 解码器的 REST API

现在让我们继续介绍带有 Spring Boot 和 Keycloak 的 OAuth2,以及需要 JWT 格式的 Bearer 访问令牌的无状态 REST API。

5.1. 依赖项

最重要的依赖项是 spring-boot-starter-oauth2-resource-server。当我们创建一个 servlet 应用程序时,我们还需要 spring-boot-starter-web

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

5.2. 配置 JWT 解码器

为了在资源服务器上验证访问令牌,我们可以选择 JWT decoder或introspection。第一个请求要求授权服务器以 JWT 格式颁发访问令牌。但是,它的效率要高得多,因为自省需要针对每个请求从资源服务器调用授权服务器。这会导致延迟,并可能使授权服务器过载。

Keycloak 访问令牌是 JWT,使用 Spring Boot,单个属性就足以使用 JWT 解码器配置资源服务器:

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://1923168.1.212:8080/realms/cemx

5.3. 将 Keycloak Realm 角色映射到 Spring 安全权限

对于带有 JWT decoder的资源服务器,我们应该configure the authentication converter.。

我们可以在这里重用以前的 authorities converter bean 的实现:

@Bean
JwtAuthenticationConverter authenticationConverter(Converter<Map<String, Object>, Collection<GrantedAuthority>> authoritiesConverter) {JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> authoritiesConverter.convert(jwt.getClaims()));return jwtAuthenticationConverter;
}

值得注意的是,这里是Access Token ,而不是ID Token

5.4. 将 SecurityFilterChain Bean 放在一起

现在,我们已准备好定义 Resource Server 安全筛选器链:

@Bean
SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http, Converter<Jwt, AbstractAuthenticationToken> jwtAuthenticationConverter) throws Exception {http.oauth2ResourceServer(resourceServer -> {resourceServer.jwt(jwtDecoder -> {jwtDecoder.jwtAuthenticationConverter(jwtAuthenticationConverter);});});http.sessionManagement(sessions -> {sessions.sessionCreationPolicy(SessionCreationPolicy.STATELESS);}).csrf(AbstractHttpConfigurer::disable);http.authorizeHttpRequests(requests -> {requests.requestMatchers("/me").authenticated();requests.anyRequest().denyAll();});return http.build();
}

在此代码中:

  • 第一个块支持使用 JWT 解码器和自定义身份验证转换器进行资源服务器配置,以将 Keycloak 角色转换为 Spring Security 权限。

  • 然后,我们完全禁用会话和针对 CSRF 的保护。

  • 最后,我们定义一些访问控制:只有具有有效 Bearer 令牌的请求才能访问 /me 端点,并且我们禁止访问任何其他资源

5.5. REST 控制器

为了演示资源服务器中的 Keycloak 角色映射,我们将在安全上下文中公开一个端点,该端点反映了 JwtAuthenticationToken 中的一些信息:

@RestController
public class MeController {@GetMapping("/me")public UserInfoDto getGreeting(JwtAuthenticationToken auth) {return new UserInfoDto(auth.getToken().getClaimAsString(StandardClaimNames.PREFERRED_USERNAME),auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());}public static record UserInfoDto(String name, List<String> roles) {}
}

这里,将身份验证强制转换为 JwtAuthenticationToken 是安全的,因为访问仅限于 isAuthenticated(),并且 JwtAuthenticationToken 是我们的身份验证转换器返回的内容

如果路由是 permitAll(),我们还应该处理 AnonymousAuthenticationToken,以防未经授权的请求 ( 那些没有 Bearer Token的请求)。

5.6. 试用 REST API

现在,我们已准备好实时试用我们的 REST API。就像我们对 Thymeleaf 应用程序所做的那样,我们可以使用我们最喜欢的 IDE 运行它,或者从 maven 父上下文执行:

sh ./mvnw -pl spring-boot-resource-server spring-boot:run

然后,要使用 Postman 从 Keycloak 获取Access Token,我们应该打开集合或请求的 Authorization 选项卡,选择 OAuth2,并使用我们已经在 Keycloak(重定向 URI)和 Spring 属性中设置的值填写表单,或者我们从 OpenID 配置 http://192.168.1.212:8080/realms/cemx/.well-known/openid-configuration中获得的值:
在这里插入图片描述

最后,我们向 http://192.168.1.128:8082/me 发送 GET 请求。响应应该是带有 Keycloak 登录和领域角色的 JSON 有效负载:
在这里插入图片描述

6. 创建 Keycloak Realm

在本教程中,我们在创建 Docker 容器时导入的自定义领域中对 Keycloak 对象进行了沙盒处理。这在团队中加入新开发人员或尝试替代配置时非常有用。

让我们详细介绍一下这些对象最初是如何创建的。

6.1. Realm*

让我们导航到左上角以单击Create Realm 按钮:
在这里插入图片描述

以下操作都是针对cemx这个realm的

6.2. 创建 Keycloak Client

现在,我们将导航到 Clients 页面,在那里我们可以创建一个名为cemc-client 的新客户端:
在这里插入图片描述

在下一步中,我们将确保Client authentication is enabled (这将使客户端支持),并且只选中Standard flow,这将激活授权代码和刷新令牌流:
在这里插入图片描述

最后,我们需要允许客户端的重定向 URI 和来源:
在这里插入图片描述

由于我们使用 oauth2Login 的 Spring Boot 客户端应用程序配置为在端口 8081 上运行,并且将 keycloak作为注册 ID,因此我们设置:

  • http://192.168.1.128:8081/login/oauth2/code/keycloak 作为 Valid redirect URIs。

  • http://192.168.1.128:8081/ 为有效的注销后 URI(Thymeleaf 索引页)

  • Web origins为+,以允许来自有效重定向 URI 的所有源

    • 允许的CORS来源。要允许所有有效重定向URI的来源,请添加“+”。但这不包括“”通配符。要允许所有来源,请明确添加“”。

6.3. 配置哪些 JSON 有效负载包含 Realm 角色

为了确保将 Keycloak 角色添加到我们在 Spring 应用程序中使用的各种有效负载中,我们导航到客户端详细信息中的 Client scopes 选项卡:
在这里插入图片描述

点击 cemc-client-dedicated 查看其配置详细信息:
在这里插入图片描述

单击 Add predefined mapper 按钮,然后选择 realm roles mapper:
在这里插入图片描述

在编辑时,我们可以选择哪些JSON 有效负载包含Realm roles:

  • access tokens:用于具有JWT Decoder的 Resource Server

  • introspection:对于具有 token introspection 的资源服务器(Spring Security 措辞中的不透明 token)

  • ID tokens::适用于具有 oauth2Login 和 OpenID 的客户端(使用 issuer-uri 自动配置提供程序)

  • userinfo:适用于具有 oauth2Login 但没有 OpenID 的客户端(issuer-uri 留空,并在 Spring conf 中设置其他提供程序属性)

因为我们例子中的 oauth2Login OpenID Client端和Resource Server 端中都实现了一些基于角色的访问控制(RBAC),所以我们应该确保将领域角色添加到访问令牌和 ID 令牌中。
在这里插入图片描述

6.4. Realm 角色

在 Keycloak 中,我们可以定义角色,并将其分配给用户,用于所有 Realm 领域或每个Client。

在本教程中,我们将重点介绍Real roles。导航到 Realm roles 页面:
在这里插入图片描述

在那里,我们创建 ADMIN 角色:
在这里插入图片描述

6.5. 用户和 Realm 角色分配

让我们转到 Users 页面添加两个用户(一个名为 admin 并被授予 ADMIN 角色,另一个名为 igor 并被授予没有 realm 角色):

我们首先创建一个名为admin 的新用户:
在这里插入图片描述

单击 Create 按钮后,我们可以看到用户详细信息。我们应该浏览到 credential 选项卡以设置其密码(123456)
在这里插入图片描述
最后,我们导航到 Role Mappings 选项卡以分配ADMIN 角色(请注意 Filter by realm roles 下拉列表):
在这里插入图片描述
创建test用户,请重复上述步骤,只不过不要分配role

6.6. 导出 Realm 数据库

完成 realm 配置后,我们可以使用以下命令将其导出(在 Docker desktop 中,使用正在运行的容器的 Exec 选项卡):

cd /opt/keycloak/bin/
sh ./kc.sh export --dir /tmp/keycloak/ --users realm_file

然后,我们可以从 Files (文件) 选项卡中收集每个领域的 JSON 文件。

7. 总结

在本文中,我们使用 Spring Boot 和 Keycloak 配置了带有 OAuth2 的后端。

除了使用 Docker 进行最小的 Keycloak 设置外,我们还了解了如何导入和导出领域。

此外,在查看了 Spring 应用程序中 OAuth2 的不同配置选项后,我们将 Spring 应用程序配置为具有 oauth2Login 的有状态 OAuth2 客户端或无状态 OAuth2 资源服务器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/26010.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GCN从理论到实践——基于PyTorch的图卷积网络层实现

Hi&#xff0c;大家好&#xff0c;我是半亩花海。图卷积网络&#xff08;Graph Convolutional Network, GCN&#xff09;是一种处理图结构数据的深度学习模型。它通过聚合邻居节点的信息来更新每个节点的特征表示&#xff0c;广泛应用于社交网络分析、推荐系统和生物信息学等领…

mapbox基础,使用geojson加载heatmap热力图层

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️heatmap热力图层样式二、🍀使用geojs…

AI数字人开发,引领科技新潮流

引言 随着人工智能技术的迅猛发展&#xff0c;AI 数字人在影视娱乐、客户服务、教育及医疗等多个领域展现出巨大的潜力。本文旨在为开发者提供一份详细的 AI 数字人系统开发指南&#xff0c;涵盖从基础架构到实现细节的各个方面&#xff0c;包括人物建模、动作生成、语音交互、…

python量化交易——金融数据管理最佳实践——qteasy创建本地数据源

文章目录 qteasy金融历史数据管理总体介绍本地数据源——DataSource对象默认数据源查看数据表查看数据源的整体信息最重要的数据表其他的数据表 从数据表中获取数据向数据表中添加数据删除数据表 —— 请尽量小心&#xff0c;删除后无法恢复&#xff01;&#xff01;总结 qteas…

使用Docker Compose部署 MySQL8

MySQL 8 是一个功能强大的关系型数据库管理系统,而 Docker 则是一个流行的容器化平台。结合使用它们可以极大地简化 MySQL 8 的部署过程,并且确保开发环境和生产环境的一致性。 安装 Docker 和 Docker Compose 首先,确保你的机器上已经安装了 Docker 和 Docker Compose。 …

axios几种请求类型的格式

Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;广泛用于浏览器和 Node.js 中发送 HTTP 请求。它支持多种请求格式&#xff0c;包括 GET、POST、PUT、DELETE 等。也叫RESTful 目录 一、axios几种请求类型的格式 1、get请求 2、post请求 3、put请求 4、delete请求 二…

网络空间安全(4)web应用程序安全要点

前言 Web应用程序安全是确保Web应用程序、服务和服务器免受网络攻击和威胁的关键环节。 一、编写安全的代码 输入验证与过滤&#xff1a;确保所有的用户输入都被正确验证和过滤&#xff0c;以防止注入攻击等安全漏洞。开发者应对URL、查询关键字、HTTP头、POST数据等进行严格的…

51页精品PPT | 农产品区块链溯源信息化平台整体解决方案

PPT展示了一个基于区块链技术的农产品溯源信息化平台的整体解决方案。它从建设背景和需求分析出发&#xff0c;强调了农产品质量安全溯源的重要性以及国际国内的相关政策要求&#xff0c;指出了食品安全问题在流通环节中的根源。方案提出了全面感知、责任到人、定期考核和追溯反…

P8649 [蓝桥杯 2017 省 B] k 倍区间--前缀和--同余定理【蓝桥杯简单题-必开long long】

P8649 [蓝桥杯 2017 省 B] k 倍区间--前缀和--同余定理 题目 分析代码 还有一件事【老爹音】 题目 分析 首先&#xff0c;看到”连续子序列求和”这一要求时&#xff0c;我们果断选择前缀和解答。 接着就要用到一个非常巧妙的“同余定理”——如果 sum[j] % K sum[i] % K&am…

Day11,Hot100(贪心算法)

贪心 &#xff08;1&#xff09;121. 买卖股票的最佳时机 第 i 天卖出的最大利润&#xff0c;即在前面最低价的时候买入 class Solution:def maxProfit(self, prices: List[int]) -> int:min_price prices[0]ans 0for price in prices:ans max(ans, price - min_price…

Spring Boot 流式响应豆包大模型对话能力

当Spring Boot遇见豆包大模型&#xff1a;一场流式响应的"魔法吟唱"仪式 一、前言&#xff1a;关于流式响应的奇妙比喻 想象一下你正在火锅店点单&#xff0c;如果服务员必须等所有菜品都备齐才一次性端上来&#xff0c;你可能会饿得把菜单都啃了。而流式响应就像贴…

2025年光电科学与智能传感国际学术会议(ICOIS 2025)

重要信息 官网&#xff1a;www.ic-icois.org 时间&#xff1a;2025年3月14-16日 地点&#xff1a;中国-长春 简介 2025年光电科学与智能传感国际学术会议&#xff08;ICOIS 2025&#xff09;将于2025年3月14-16日在中国-长春隆重召开。会议将围绕“光学光电”、“智能传感”…

【SpringBoot】数据访问技术spring Data、 JDBC、MyBatis、JSR-303校验

Spring Boot 数据访问技术及特性 目录标题 Spring Boot 数据访问技术及特性摘要1. 引言2. Spring Data架构与原理2.1 Spring Data概述2.2 Spring Data核心组件2.3 Spring Boot与Spring Data的集成机制 3. Spring Boot与JDBC的整合3.1 JDBC整合流程3.2 数据源自动配置3.3 JdbcTe…

Jmeter插件下载及安装

1、在Jmeter官网&#xff08;Install :: JMeter-Plugins.org&#xff09;下载所需插件 2、将下载的插件复制到jmeter文件下的lib/ext文件里&#xff08;PS&#xff1a;D:\Jmeter\apache-jmeter-5.6.2\lib\ext&#xff09; 3、打开Jmeter&#xff0c;选择 选项----Plugins Manag…

PostgreSQL10 逻辑复制实战:构建高可用数据同步架构!

PostgreSQL10 逻辑复制实战&#xff1a;打造高可用数据同步架构&#xff01; 概述 PostgreSQL 10 引入了逻辑复制&#xff08;Logical Replication&#xff09;&#xff0c;为数据库高可用和数据同步提供了更灵活的选择。PostgreSQL 复制机制主要分为物理复制和逻辑复制两种&…

OptiTrack光学跟踪系统:引领工厂机器人应用的革新浪潮

在现代化的工厂生产线上&#xff0c;一台机械臂正以惊人的毫米级精度执行着精密零件的装配任务。这一精准操作的背后&#xff0c;是OptiTrack光学跟踪系统的实时捕捉与优化&#xff0c;它正助力生产效率与产品质量迈向新的高度。如今&#xff0c;这一技术正在全球范围内广泛应用…

Prometheus + Grafana 监控

Prometheus Grafana 监控 官网介绍&#xff1a;Prometheus 是一个开源系统 监控和警报工具包最初由 SoundCloud 构建。自 2012 年成立以来&#xff0c;许多 公司和组织已经采用了 Prometheus&#xff0c;并且该项目具有非常 活跃的开发人员和用户社区。它现在是一个独立的开源…

常见排序算法

1.插入排序 直接插入排序 思想&#xff1a;将待排序的元素插入到有序序列中&#xff0c;并保持有序&#xff0c;直到所有待排序元素插入完为止&#xff0c;得到一个新的有序序列。 //升序 void InsertSort(int* a, int n) {for (int i 1; i < n; i){int end i - 1;int tm…

【MATLAB例程】三维下的IMM(交互式多模型),模型使用CV(匀速)和CA(匀加速)

给出三维下的交互式多模型&#xff08;IMM&#xff09;matlab例程&#xff0c;模型使用匀速运动CV和匀加速运动CA&#xff0c;滤波使用EKF&#xff08;扩展卡尔曼滤波&#xff09; 文章目录 代码运行结果程序结构 代码讲解模型定义&#xff1a;轨迹生成&#xff1a;IMM核心流程…

网络安全 越权分为几种

1. 权限查看 Linux 系统中的每个文件和目录都有访问许可权限&#xff0c;通过其确定谁可以通过何种方式对文件和目录进行访问和操作。 文件或目录的访问权限分为只读、只写和可执行3种。以文件为例&#xff0c;只读权限表示只允许读其内容&#xff0c;而禁止对其做任何的更改…