SpringBoot整合Keycloak的項目實踐
1、概覽
本文將帶你了解如何設置 Keycloak 服務器,以及如何使用 Spring Security OAuth2.0 將Spring Boot應用連接到 Keycloak 服務器。
2、Keycloak 是什么?
Keycloak是針對現(xiàn)代應用和服務的開源身份和訪問管理解決方案。
Keycloak 提供了諸如單點登錄(SSO)、身份代理和社交登錄、用戶聯(lián)盟、客戶端適配器、管理控制臺和賬戶管理等功能。
本文使用 Keycloak 的管理控制臺,使用 Spring Security OAuth2.0 設置和連接 Spring Boot。
3、設置 Keycloak 服務器
設置和配置 Keycloak 服務器。
3.1、下載和安裝 Keycloak
有多種發(fā)行版可供選擇,本文使 Keycloak-22.0.3 獨立服務器發(fā)行版。點擊這里從官方下載。
下載完后,解壓縮并從終端啟動 Keycloak:
unzip keycloak-22.0.3.zip cd keycloak-22.0.3 bin/kc.sh start-dev
運行這些命令后,Keycloak 會啟動服務。如果你看到一行類似于Keycloak 22.0.3 [...] started的內(nèi)容,就表示服務器啟動成功。
打開瀏覽器,訪問http://localhost:8080,會被重定向到http://localhost:8080/auth以創(chuàng)建管理員進行登錄:

創(chuàng)建一個名為initial1的初始管理員用戶,密碼為zaq1!QAZ。點擊 “Create”后,可以看到 “User Created” 的提示信息。
現(xiàn)在進入管理控制臺。在登錄頁面,輸入initial管理員用戶憑證:

3.2、創(chuàng)建 Realm
登錄成功后,進入控制臺,默認為MasterRealm。
導航到左上角,找到 “Create realm” 按鈕:

點擊它,添加一個名為SpringBootKeycloak的新 Realm:

單擊 “Create” 按鈕,創(chuàng)建一個新的 Realm。會被重定向到該 Realm。接下來的所有操作都將在這個新的SpringBootKeycloakRealm 中執(zhí)行。
3.3、創(chuàng)建客戶端
現(xiàn)在進入 “Clients” 頁面。如下圖所示,Keycloak 已經(jīng)內(nèi)置了客戶端:

我們需要在應用中添加一個新客戶端,點擊 “Create”,將新客戶端命名為login-app:

在下一步的設置中,除了 “Valid Redirect URIs” 字段外,其他字段保留所有默認值。該字段包含將使用此客戶端進行身份驗證的應用 URL:

稍后,我們會創(chuàng)建一個運行于 8081 端口的 Spring Boot 應用,該應用將使用該客戶端。因此,在上面使用了http://localhost:8081/的重定向 URL。
3.4、創(chuàng)建角色和用戶
Keycloak 使用基于角色的訪問;因此,每個用戶都必須有一個角色。
進入 “Realm Roles” 頁面:

然后添加用戶角色:

現(xiàn)在有了一個可以分配給用戶的角色,但由于還沒有用戶,讓我們?nèi)?“Users” 頁面添加一個:

添加一個名為user1的用戶:

用戶創(chuàng)建后,會顯示一個包含其詳細信息的頁面:

現(xiàn)在進入 “Credentials” 選項卡。把初始密碼設置為xsw2@WS:

最后,進入 “Role Mappings” 選項卡。為user1分配用戶角色:

4、使用 Keycloak API 生成 Access Token
Keycloak 提供了用于生成和刷新 Access Token 的 REST API,可用于創(chuàng)建自己的登錄頁面。
首先,向如下 URL 發(fā)送 POST 請求,從 Keycloak 獲取 Access Token:
http://localhost:8080/realms/SpringBootKeycloak/protocol/openid-connect/token
請求體應包含x-www-form-urlencoded格式的參數(shù):
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 發(fā)送 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、創(chuàng)建和配置 Spring Boot 應用
創(chuàng)建一個 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>
現(xiàn)在,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。它可以根據(jù) Schemaspring.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 管理控制臺中創(chuàng)建的 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、配置類
創(chuàng)建SecurityFilterChainBean 來配置HttpSecurity。使用http.oauth2Login()啟用 OAuth2 登錄。
創(chuàng)建 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方法將根據(jù) Keycloak 服務器驗證綁定的 JWT Token。
在configure()方法中根據(jù)權限和角色配置訪問權限。這些約束條件可確保對/customers/*的每個請求只有在請求者是具有USER角色的經(jīng)過身份驗證的用戶時才會獲得授權。
最后,添加了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;
}
@Override
public 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類實現(xiàn)了LogoutHandler,并向 Keycloak 發(fā)送注銷請求。
現(xiàn)在,通過身份驗證后,就可以訪問內(nèi)部 customers 頁面了。
5.4、Thymeleaf Web 頁面
使用 Thymeleaf 渲染頁面。
有三個頁面:
external.html- 面向外部的頁面customers.html- 面向內(nèi)部的頁面,其訪問權限僅限于具有user角色的認證用戶layout.html- 一個簡單的布局,由兩個片段組成,分別用于面向外部的頁面和面向內(nèi)部的頁面
Thymeleaf 模板的代碼可在Github上獲取。
5.5、Controller
Web Controller 會將內(nèi)部和外部 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)作為原始數(shù)據(jù)來顯示,僅此而已。
6、演示
現(xiàn)在,測試應用。通過集成開發(fā)環(huán)境(如 Spring Tool Suite - STS)運行 Spring Boot 應用,或者在終端運行如下命令:
mvn clean spring-boot:run
訪問http://localhost:8081,如下:

現(xiàn)在,點擊 “customers” 戶進入內(nèi)部頁面,這是敏感信息的位置。
然后會被重定向到通過 Keycloak 進行身份驗證,以檢查我們是否被授權查看此內(nèi)容:

用user1的憑證登錄,Keycloak 會驗證我們的授權,確認我們擁有用戶角色,然后會被重定向到受限的 “customers” 頁面:

現(xiàn)在,整個流程已經(jīng)完畢了。你可以看到,Spring Boot 無縫地處理了調(diào)用 Keycloak 授權服務器的整個過程。我們無需調(diào)用 Keycloak API 自己生成 Access Token,甚至無需在請求受保護資源時明確發(fā)送Authorization頭。
7、總結
本文介紹了如何如何設置了 Keycloak 服務器,以及如何在 Spring Boot 中使用 Spring Security OAuth2.0 結合 Keycloak 實現(xiàn)認證和授權。
到此這篇關于SpringBoot整合Keycloak的項目實踐的文章就介紹到這了,更多相關SpringBoot整合Keycloak內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Springboot詳細講解RocketMQ實現(xiàn)順序消息的發(fā)送與消費流程
RocketMQ作為一款純java、分布式、隊列模型的開源消息中間件,支持事務消息、順序消息、批量消息、定時消息、消息回溯等,本篇我們了解如何實現(xiàn)順序消息的發(fā)送與消費2022-06-06
SpringBoot 下集成緩存工具類 CacheManager
這篇文章主要介紹了Springboot下集成緩存工具類CacheManager,想進一步了解相關知識的同學,可以詳細閱讀本文2023-03-03

