Spring?Boot應(yīng)用程序中如何使用Keycloak詳解
正文
在這篇文章中,我將展示如何在 Spring Boot 應(yīng)用程序中使用 Keycloak。在我們使用 Keycloak 之前,我們將介紹一些關(guān)于 Keycloak 是什么以及我們?yōu)槭裁词褂盟幕A(chǔ)知識。
要開始前,您需要具備以下條件:
- 代碼編輯器——IntelliJ
- 數(shù)據(jù)庫——MySQL
- Keycloak
- Java 8
什么是Keycloak?
Keycloak是一種用于現(xiàn)代應(yīng)用程序和服務(wù)的開源身份和訪問管理解決方案。Keycloak 同時提供 SAML 和 OpenID 協(xié)議解決方案。
我們?yōu)槭裁匆褂肒eycloak?
如前所述,Keycloak 提供身份和訪問管理,它也是開源的。SAML 和 OpenID 協(xié)議是行業(yè)標(biāo)準(zhǔn)。構(gòu)建與 Keycloak 集成的應(yīng)用程序只會為您提供更安全和穩(wěn)定的解決方案。當(dāng)然還有其他可用的解決方案,如 Gluu、Shibboleth、WSO2。
對于這篇文章,我們將使用 Keycloak。
在Spring Boot 應(yīng)用程序中使用keycloak
這個演示有兩個部分。一個是關(guān)于Keycloak。第二個是關(guān)于使用 Keycloak 保護(hù) Spring Boot 應(yīng)用程序。
安裝Keycloak
在您的機(jī)器上下載Keycloak 。解壓縮下載的文件并使用命令提示符下 bin 目錄中的以下命令運(yùn)行服務(wù)器(注意 – 我在 Windows 機(jī)器上):
standalone.bat -Djboss.socket.binding.port-offset=100
這將在本地計(jì)算機(jī)上為您的 Keycloak 啟動 Wildfly 服務(wù)器。我們可以通過執(zhí)行 URL 來訪問服務(wù)器http://localhost:8180
。如果您只是使用 standalone.bat 執(zhí)行而沒有該參數(shù),則服務(wù)器將在端口上運(yùn)行8080
。
啟動服務(wù)器后,您要做的第一件事就是創(chuàng)建一個管理員用戶。我們將創(chuàng)建一個用戶 admin 和密碼 d#n3q2b 。
現(xiàn)在我們將訪問管理控制臺并輸入我們的用戶詳細(xì)信息。一旦我們以管理員用戶身份登錄,我們將看到如下第一個屏幕:
添加應(yīng)用程序
初始屏幕顯示默認(rèn)領(lǐng)域。出于演示目的,我們將創(chuàng)建一個新領(lǐng)域SpringBootKeycloakApp
。在這個領(lǐng)域中,我們將添加我們的 Spring Boot 應(yīng)用程序作為客戶端。在“客戶端”選項(xiàng)卡上創(chuàng)建一個新客戶端。我們將我們的客戶端應(yīng)用程序命名為 SpringBootApp。
現(xiàn)在在設(shè)置中,我們將為我們的 Spring Boot 應(yīng)用程序添加重定向 url。這是 Keycloak 將在身份驗(yàn)證后重定向到我們的應(yīng)用程序的 URL。此外,我們使用 openid connect 作為協(xié)議作為此實(shí)現(xiàn)的一部分。
添加用戶
現(xiàn)在我們將添加一個我們將用于身份驗(yàn)證的用戶。我們將使用此用戶登錄到我們的示例 Spring Boot 應(yīng)用程序。
在 Keycloak 的角色選項(xiàng)卡上為該用戶添加您想要的角色ROLE_User
。完成后,讓我們轉(zhuǎn)到“用戶”選項(xiàng)卡并添加一個新用戶。
在 Role Mappings 選項(xiàng)卡上,確保為該用戶添加新創(chuàng)建的角色。
創(chuàng)建 Spring Boot 應(yīng)用程序
現(xiàn)在,我們將創(chuàng)建一個簡單的 Spring Boot 應(yīng)用程序,它將使用 Keycloak 來確保安全。作為此應(yīng)用程序的一部分,我們將為將通過應(yīng)用程序進(jìn)行身份驗(yàn)證的用戶顯示待辦事項(xiàng)列表任務(wù)。
要構(gòu)建此應(yīng)用程序,我們需要以下依賴項(xiàng):
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.keycloak:keycloak-spring-boot-starter' runtimeOnly 'mysql:mysql-connector-java' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } testImplementation 'org.springframework.security:spring-security-test' }
如您所見,我們正在使用spring-boot
andspring-security
以及keycloak-spring-boot-starter
依賴項(xiàng)。
keycloak 依賴項(xiàng)包括 Keycloak 客戶端適配器。我們將使用這些適配器進(jìn)行身份驗(yàn)證。它們將取代我們的標(biāo)準(zhǔn) Spring Security 適配器。為了確保此keycloak-spring-boot-starter
依賴項(xiàng)正常工作,我們需要在我們的 gradle 文件中添加一個依賴項(xiàng),如下所示:
dependencyManagement { imports { mavenBom "org.keycloak.bom:keycloak-adapter-bom:11.0.2" } }
要了解更多相關(guān)信息,您可以訪問keycloak的官方文檔。
我們的 Controller 類將有兩個重要的方法,一個是獲取任何人都可以訪問的主頁,另一個是獲取任務(wù)列表,只有具有 ROLE_User 角色的經(jīng)過身份驗(yàn)證的用戶才能訪問這些任務(wù)。此 TaskController 的代碼如下所示:
package com.betterjavacode.keycloakdemo.keycloakdemo.controllers; import com.betterjavacode.keycloakdemo.keycloakdemo.dto.TaskDto; import com.betterjavacode.keycloakdemo.keycloakdemo.managers.TaskManager; import org.keycloak.KeycloakSecurityContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletRequest; import java.util.List; @Controller public class TaskController { private final HttpServletRequest request; @Autowired public TaskController(HttpServletRequest request) { this.request = request; } @Autowired private TaskManager taskManager; @GetMapping(value="/") public String home() { return "index"; } @GetMapping(value="/tasks") public String getTasks(Model model) { List tasks = taskManager.getAllTasks(); model.addAttribute("tasks", tasks); model.addAttribute("name", getKeycloakSecurityContext().getIdToken().getGivenName()); return "tasks"; } private KeycloakSecurityContext getKeycloakSecurityContext() { return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); } }
在這個控制器類中,我們用來TaskManager
獲取所有任務(wù)。我會解釋 KeyCloakSecurityContext
我什么時候會展示SecurityConfig
。
有沒有使用Spring-Security
我們可以利用此應(yīng)用程序并使用 Keycloak 進(jìn)行身份驗(yàn)證,無論是否使用Spring-Security
. 作為本演示的一部分,我們使用Spring-Security
. 要在沒有 Spring-Security 的情況下使用相同的應(yīng)用程序,您只需刪除 Spring-Security 依賴并通過application.properties
文件添加安全配置。
我們需要以下屬性才能application.properties
在此應(yīng)用程序中使用 Keycloak 進(jìn)行身份驗(yàn)證。
keycloak.auth-server-url=http://localhost:8180/auth keycloak.realm=SpringBootKeycloakApp keycloak.resource=SpringBootApp keycloak.public-client=true keycloak.principal-attribute=preferred_username
如果我們想在沒有 Spring-Security 的情況下使用這個應(yīng)用程序,我們還需要以下兩個屬性:
keycloak.security-constraints[0].authRoles[0]=ROLE_User keycloak.security-constraints[0].securityCollections[0].patterns[0]=/tasks
由于我們正在使用Spring-Security
,所以我們將通過一個 Java 類來配置安全配置SecurityConfig
。
這個 SecurityConfig 類將擴(kuò)展KeyCloakWebSecurityConfigurerAdapter
.
我們的配置方法如下所示:
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { super.configure(httpSecurity); httpSecurity.authorizeRequests() .antMatchers("/tasks").hasRole("User") .anyRequest().permitAll(); }
基本上任何到達(dá) /tasks 端點(diǎn)的請求都應(yīng)該具有 ROLE_User 用戶角色。此處假定 ROLE_ 的前綴。除任何其他請求外,未經(jīng)任何授權(quán)將被允許。在這種情況下,我們將調(diào)用我們的索引頁面。
我們將使用@KeyCloakConfiguration
基本上是封面@Configuration
和@EnableWebSecurity
注釋的注釋。
由于我們的SecurityConfig
extends KeycloakWebSecurityConfigurerAdapter
,我們必須實(shí)施 sessionAuthenticationStrategy 和 httpSessionManager 。我們還必須使用 Spring Security Authentication Manager 注冊我們的 idp Keycloak。
所以我們的 SecurityConfig 將如下所示:
package com.betterjavacode.keycloakdemo.keycloakdemo.config; import org.keycloak.adapters.springsecurity.KeycloakConfiguration; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.keycloak.adapters.springsecurity.management.HttpSessionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @KeycloakConfiguration public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) { SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper(); simpleAuthorityMapper.setPrefix("ROLE_"); KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(simpleAuthorityMapper); authenticationManagerBuilder.authenticationProvider(keycloakAuthenticationProvider); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy () { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Bean @Override @ConditionalOnMissingBean(HttpSessionManager.class) protected HttpSessionManager httpSessionManager() { return new HttpSessionManager(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { super.configure(httpSecurity); httpSecurity.authorizeRequests() .antMatchers("/tasks").hasRole("User") .anyRequest().permitAll(); } }
所以 Spring Security 使用像 ROLE_USER 這樣大寫的角色,并且總是使用 ROLE_ 前綴。為了處理這個問題,我在 Keycloak 中添加了一個角色為 ROLE_User 的用戶,但我們只會驗(yàn)證一個前綴,因?yàn)槲覀兊?http 配置無論如何都會驗(yàn)證該角色。
由于我們將使用 Keycloak 進(jìn)行身份驗(yàn)證,因此我們需要一個用于用戶狀態(tài)的會話。我們在這里使用RegisterSessionAuthenticationStrategy
。HttpSessionManager
是一個條件 bean,因?yàn)?Keycloak 已經(jīng)實(shí)現(xiàn)了那個 bean。
要實(shí)現(xiàn) Keycloak Spring Boot 適配器,我們將添加一個KeyCloakSpringBootConfigResolver
bean,如下所示:
package com.betterjavacode.keycloakdemo.keycloakdemo.config; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class KeycloakConfig { @Bean public KeycloakSpringBootConfigResolver keycloakSpringBootConfigResolver() { return new KeycloakSpringBootConfigResolver(); } }
應(yīng)用程序演示
運(yùn)行我們的 keycloak 應(yīng)用程序,它將在http://localhost:8180上運(yùn)行。我們的 Spring Boot 應(yīng)用程序?qū)⒃趆ttp://localhost:8080運(yùn)行。
我們的 Spring Boot 應(yīng)用程序的第一個屏幕如下所示:
現(xiàn)在,如果用戶點(diǎn)擊獲取所有任務(wù),他將被重定向到 Keycloak 登錄屏幕,如下所示:
現(xiàn)在,我將輸入我的用戶 betterjavacode 用戶名和密碼,它將向我們顯示我們的任務(wù)列表,如下所示:
認(rèn)證流程
當(dāng)用戶單擊“獲取所有任務(wù)”時,用戶將被重定向到 Spring Security 的 sso/login 端點(diǎn),KeycloakSpringBootConfigResolver 處理該端點(diǎn)并向 Keycloak 發(fā)送授權(quán)代碼流請求
http://localhost:8180/auth/realms/SpringBootKeycloakApp/protocol/openid-connect/auth?response_type=code&client_id=SpringBootApp&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsso%2Flogin&state=70bd4e28-89e6-43b8-8bea-94c6d057a5cf&login=true&scope=openid
Keycloak 將處理請求以響應(yīng)會話代碼并顯示登錄屏幕。
一旦用戶輸入憑據(jù)并且 keycloak 驗(yàn)證了這些憑據(jù),它將使用授權(quán)代碼進(jìn)行響應(yīng),并將此代碼交換為令牌,然后用戶登錄。
結(jié)論
在這篇文章中,展示了如何使用 Keycloak 作為身份提供者來保護(hù)您的 Spring Boot 應(yīng)用程序。這樣 一個簡單的程序就完成了!
以上就是Spring Boot應(yīng)用程序中如何使用Keycloak詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Boot使用Keycloak的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java將對象寫入文件讀出_序列化與反序列化的實(shí)例
下面小編就為大家?guī)硪黄狫ava將對象寫入文件讀出_序列化與反序列化的實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08線程池調(diào)用kafka發(fā)送消息產(chǎn)生的內(nèi)存泄漏問題排查解決
這篇文章主要為大家介紹了線程池調(diào)用kafka發(fā)送消息產(chǎn)生的內(nèi)存泄漏問題排查解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08詳解Java的文件與目錄管理以及輸入輸出相關(guān)操作
這篇文章主要介紹了詳解Java的文件與目錄管理以及輸入輸出相關(guān)操作,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09Java class文件格式之屬性_動力節(jié)點(diǎn)Java學(xué)院整理
在本文中, 主要講解了class文件中的一些屬性。 這些屬性可以出現(xiàn)在class文件中的對個地方, 用來描述一些其他信息2017-06-06