当前位置: 首页 > news >正文

Spring Boot 整合 Keycloak

1、概览

本文将带你了解如何设置 Keycloak 服务器,以及如何使用 Spring Security OAuth2.0 将 Spring Boot 应用连接到 Keycloak 服务器。

2、Keycloak 是什么?

Keycloak 是针对现代应用和服务的开源身份和访问管理解决方案。

Keycloak 提供了诸如单点登录(SSO)、身份代理和社交登录、用户联盟、客户端适配器、管理控制台和账户管理等功能。

本文使用 Keycloak 的管理控制台,使用 Spring Security OAuth2.0 设置和连接 Spring Boot。

3、设置 Keycloak 服务器

设置和配置 Keycloak 服务器。

3.1、下载和安装 Keycloak

有多种发行版可供选择,本文使 Keycloak-22.0.3 独立服务器发行版。点击 这里 从官方下载。

下载完后,解压缩并从终端启动 Keycloak:

unzip keycloak-22.0.3.zip 
cd keycloak-22.0.3
bin/kc.sh start-dev

运行这些命令后,Keycloak 会启动服务。如果你看到一行类似于 Keycloak 22.0.3 [...] started 的内容,就表示服务器启动成功。

打开浏览器,访问 http://localhost:8080,会被重定向到 http://localhost:8080/auth 以创建管理员进行登录:

创建一个名为 initial1 的初始管理员用户,密码为 zaq1!QAZ。点击 “Create”后,可以看到 “User Created” 的提示信息。

现在进入管理控制台。在登录页面,输入 initial 管理员用户凭证:

3.2、创建 Realm

登录成功后,进入控制台,默认为 Master Realm。

导航到左上角,找到 “Create realm” 按钮:

点击它,添加一个名为 SpringBootKeycloak 的新 Realm:

单击 “Create” 按钮,创建一个新的 Realm。会被重定向到该 Realm。接下来的所有操作都将在这个新的 SpringBootKeycloak Realm 中执行。

3.3、创建客户端

现在进入 “Clients” 页面。如下图所示,Keycloak 已经内置了客户端:

我们需要在应用中添加一个新客户端,点击 “Create”,将新客户端命名为 login-app

在下一步的设置中,除了 “Valid Redirect URIs” 字段外,其他字段保留所有默认值。该字段包含将使用此客户端进行身份验证的应用 URL:

稍后,我们会创建一个运行于 8081 端口的 Spring Boot 应用,该应用将使用该客户端。因此,在上面使用了 http://localhost:8081/ 的重定向 URL。

3.4、创建角色和用户

Keycloak 使用基于角色的访问;因此,每个用户都必须有一个角色。

进入 “Realm Roles” 页面:

然后添加用户角色:

现在有了一个可以分配给用户的角色,但由于还没有用户,让我们去 “Users” 页面添加一个:

添加一个名为 user1 的用户:

用户创建后,会显示一个包含其详细信息的页面:

现在进入 “Credentials” 选项卡。把初始密码设置为 xsw2@WS

最后,进入 “Role Mappings” 选项卡。为 user1 分配用户角色:

4、使用 Keycloak API 生成 Access Token

Keycloak 提供了用于生成和刷新 Access Token 的 REST API,可用于创建自己的登录页面。

首先,向如下 URL 发送 POST 请求,从 Keycloak 获取 Access Token:

http://localhost:8080/realms/SpringBootKeycloak/protocol/openid-connect/token

请求体应包含 x-www-form-urlencoded 格式的参数:

client_id:<your_client_id>
username:<your_username>
password:<your_password>
grant_type:password

这会得到一个 access_token 和一个 refresh_token

每次请求受 Keycloak 保护的资源时,都应使用 Access Token,只需将其放在 Authorization 头中即可:

headers: {'Authorization': 'Bearer' + access_token
}

Access Token 过期后,可以通过向上述相同的 URL 发送 POST 请求来刷新 Access Token,但请求中应包含 Refresh Token,而不是用户名和密码:

{'client_id': 'your_client_id','refresh_token': refresh_token_from_previous_request,'grant_type': 'refresh_token'
}

Keycloak 会响应新的 access_token 和 refresh_token

5、创建和配置 Spring Boot 应用

创建一个 Spring Boot 应用,并将其配置为 OAuth 客户端,与 Keycloak 服务器进行交互。

5.1、依赖

使用 Spring Security OAuth2.0 客户端连接到 Keycloak 服务器。

首先,在 pom.xml 中声明 spring-boot-starter-oauth2-client 和 spring-boot-starter-security 依赖:

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

使用 spring-boot-starter-oauth2-resource-server 将身份验证控制委托给 Keycloak 服务器。它允许我们使用 Keycloak 服务器验证 JWT Token:

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

现在,Spring Boot 应用可以与 Keycloak 交互了。

5.2、Keycloak 配置

将 Keycloak 客户端视为 OAuth 客户端。因此,需要配置 Spring Boot 应用以使用 OAuth 客户端。

ClientRegistration 类保存客户端的所有基本信息。Spring 自动配置会查找模式为 spring.security.oauth2.client.registration.[registrationId] 的属性,并使用 OAuth 2.0 或 OpenID Connect(OIDC) 注册客户端。

客户端注册配置:

spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid

在 client-id 中指定的值与我们在管理控制台中命名的客户端相匹配。

Spring Boot 应用需要与 OAuth 2.0 或 OIDC Provider 交互,以处理不同授权方式的实际请求逻辑。因此,需要配置 OIDC Provider。它可以根据 Schema spring.security.oauth2.client.provider.[provider name] 的属性值自动配置。

OIDC Provider 配置:

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

在 issuer-uri 中指定路径(我们是在 8080 端口启动 Keycloak 的)。该属性标识了授权服务器的基本 URI,输入在 Keycloak 管理控制台中创建的 Realm 名称。此外,还可以将 user-name-attribute 定义为 preferred_username,以便在 Controller 的 Principal 中填充合适的用户。

最后,添加针对 Keycloak 服务器验证 JWT Token 所需的配置:

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak

5.3、配置类

创建 SecurityFilterChain Bean 来配置 HttpSecurity。使用 http.oauth2Login() 启用 OAuth2 登录。

创建 Security 配置:

@Configuration 
@EnableWebSecurity 
class SecurityConfig { private final KeycloakLogoutHandler keycloakLogoutHandler; SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) { this.keycloakLogoutHandler = keycloakLogoutHandler; } @Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Order(1) @Bean public SecurityFilterChain clientFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .requestMatchers(new AntPathRequestMatcher("/")) .permitAll() .anyRequest() .authenticated(); http.oauth2Login() .and() .logout() .addLogoutHandler(keycloakLogoutHandler) .logoutSuccessUrl("/"); return http.build(); } @Order(2) @Bean public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .requestMatchers(new AntPathRequestMatcher("/customers*")) .hasRole("USER") .anyRequest() .authenticated(); http.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));return http.build(); } @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { return http.getSharedObject(AuthenticationManagerBuilder.class) .build(); } 
}

在上面的代码中,oauth2Login() 方法将 OAuth2LoginAuthenticationFilter 添加到过滤器链中。该过滤器会拦截请求并应用 OAuth 2 身份验证所需的逻辑。oauth2ResourceServer 方法将根据 Keycloak 服务器验证绑定的 JWT Token。

在 configure() 方法中根据权限和角色配置访问权限。这些约束条件可确保对 /customers/* 的每个请求只有在请求者是具有 USER 角色的经过身份验证的用户时才会获得授权。

最后,添加了 KeycloakLogoutHandler 类来处理 Keycloak 注销:

@Component
public class KeycloakLogoutHandler implements LogoutHandler {private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);private final RestTemplate restTemplate;public KeycloakLogoutHandler(RestTemplate restTemplate) {this.restTemplate = restTemplate;}@Overridepublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) {logoutFromKeycloak((OidcUser) auth.getPrincipal());}private void logoutFromKeycloak(OidcUser user) {String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(endSessionEndpoint).queryParam("id_token_hint", user.getIdToken().getTokenValue());ResponseEntity<String> logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class);if (logoutResponse.getStatusCode().is2xxSuccessful()) {logger.info("Successfulley logged out from Keycloak");} else {logger.error("Could not propagate logout to Keycloak");}}}

KeycloakLogoutHandler 类实现了 LogoutHandler,并向 Keycloak 发送注销请求。

现在,通过身份验证后,就可以访问内部 customers 页面了。

5.4、Thymeleaf Web 页面

使用 Thymeleaf 渲染页面。

有三个页面:

  • external.html - 面向外部的页面
  • customers.html - 面向内部的页面,其访问权限仅限于具有 user 角色的认证用户
  • layout.html - 一个简单的布局,由两个片段组成,分别用于面向外部的页面和面向内部的页面

Thymeleaf 模板的代码可在 Github 上获取。

5.5、Controller

Web Controller 会将内部和外部 URL 映射到相应的 Thymeleaf 模板:

@GetMapping(path = "/")
public String index() {return "external";
}@GetMapping(path = "/customers")
public String customers(Principal principal, Model model) {addCustomers();model.addAttribute("customers", customerDAO.findAll());model.addAttribute("username", principal.getName());return "customers";
}

/customers 会从 Repository 中检索所有客户,并将结果作为属性添加到 Model 中。之后,在 Thymeleaf 中遍历结果。

为了能够显示用户名,还注入了 Principal

注意,这里只是将客户(customers)作为原始数据来显示,仅此而已。

6、演示

现在,测试应用。通过集成开发环境(如 Spring Tool Suite - STS)运行 Spring Boot 应用,或者在终端运行如下命令:

mvn clean spring-boot:run

访问 http://localhost:8081,如下:

现在,点击 “customers” 户进入内部页面,这是敏感信息的位置。

然后会被重定向到通过 Keycloak 进行身份验证,以检查我们是否被授权查看此内容:

用 user1 的凭证登录,Keycloak 会验证我们的授权,确认我们拥有用户角色,然后会被重定向到受限的 “customers” 页面:

现在,整个流程已经完毕了。你可以看到,Spring Boot 无缝地处理了调用 Keycloak 授权服务器的整个过程。我们无需调用 Keycloak API 自己生成 Access Token,甚至无需在请求受保护资源时明确发送 Authorization 头。

7、总结

本文介绍了如何如何设置了 Keycloak 服务器,以及如何在 Spring Boot 中使用 Spring Security OAuth2.0 结合 Keycloak 实现认证和授权。


Ref:https://www.baeldung.com/spring-boot-keycloak

相关文章:

  • K8s flink-operator 例子
  • 多无人机通信(多机通信)+配置ssh服务
  • opengauss使用遇到的问题,随时更新
  • react是一种语言?
  • 高效编程的利器 Jupyter Notebook
  • (undone) MIT6.824 Lecture1 笔记
  • 数据结构:树(并查集)
  • minio 快速入门+单机部署+集群+调优
  • 前端使用xlsx-js-style导出Excel,带样式,并处理合并单元格边框显示不全和动态插入表头解决
  • 分治思想--python
  • Nest.js实现一个简单的聊天室
  • 24.9.27学习笔记
  • WebRTC关键技术及应用场景:EasyCVR视频汇聚平台高效低延迟视频监控解决方案
  • C++:模拟实现string
  • 如何使用 WebRTC 获取摄像头视频
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【css3】浏览器内核及其兼容性
  • 【刷算法】求1+2+3+...+n
  • Android Volley源码解析
  • Apache Spark Streaming 使用实例
  • codis proxy处理流程
  • Lucene解析 - 基本概念
  • React-Native - 收藏集 - 掘金
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • Service Worker
  • SpiderData 2019年2月13日 DApp数据排行榜
  • Swoft 源码剖析 - 代码自动更新机制
  • webpack+react项目初体验——记录我的webpack环境配置
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 聊聊hikari连接池的leakDetectionThreshold
  • 前端技术周刊 2019-01-14:客户端存储
  • 使用API自动生成工具优化前端工作流
  • 使用SAX解析XML
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • !!java web学习笔记(一到五)
  • # 达梦数据库知识点
  • # 职场生活之道:善于团结
  • #Linux(Source Insight安装及工程建立)
  • #单片机(TB6600驱动42步进电机)
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #控制台大学课堂点名问题_课堂随机点名
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (回溯) LeetCode 46. 全排列
  • (三)模仿学习-Action数据的模仿
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (一)Dubbo快速入门、介绍、使用
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .net core webapi 大文件上传到wwwroot文件夹