Keycloak 提供了客戶端適配器來方便各應用程序的接入,本文主要介紹在 Tomcat 和 SpringBoot 應用中如何使用 Keycloak 來保護資源;文中使用到的軟件版本:Java 1.8.0_191、Keycloak 1.16.1、Tomcat 8 .5.76、SpringBoot 2.5.9。
1、Keycloak 地址
這里假設 Keycloak 已安裝完成,地址為 http://10.49.196.10:8080/auth。Keycloak 的安裝方法可以參考:Keycloak 入門實戰(2)--安裝。
2、Tomcat 中使用 Keycloak
2.1、下載 Tomcat 的適配器
https://www.keycloak.org/archive/downloads-16.1.1.html
2.2、安裝適配器
把下載的文件(keycloak-oidc-tomcat-adapter-16.1.1.tar.gz)解壓到 Tomcat 的 lib 目錄下。
2.3、設置 Keycloak Valve
在 web 應用的 META-INF 目錄下創建 context.xml 文件:
<Context> <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/> </Context>
2.4、生成 keycloak.json 文件
假設自己 web 應用的 context-path 為 /keycloak-tomcat,在 Keycloak 中創建一個客戶端:client-tomcat
在該客戶端的安裝 tab 頁,格式選 “Keycloak OIDC JSON”,點擊 “下載” 按鈕。
把下載下來的 keycloak.json 文件拷貝到 web 應用的 WEB-INF 目錄下。
2.5、修改應用 web.xml 文件定義資源保護規則
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <security-constraint> <!--登出url不需要保護--> <web-resource-collection> <web-resource-name>logout</web-resource-name> <url-pattern>/logout.jsp</url-pattern> </web-resource-collection> </security-constraint> <security-constraint> <!--被保護的資源--> <web-resource-collection> <web-resource-name>admin</web-resource-name> <url-pattern>/admin/*</url-pattern> <url-pattern>/system/*</url-pattern> </web-resource-collection> <!--可以訪問保護資源的角色,這里出現的角色需要在 security-role 中聲明--> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <security-constraint> <!--被保護的資源--> <web-resource-collection> <web-resource-name>user</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <!--可以訪問保護資源的角色,這里出現的角色需要在 security-role 中聲明--> <auth-constraint> <role-name>user</role-name> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <!--登錄方法--> <auth-method>KEYCLOAK</auth-method> <!--領域名稱,可為空--> <realm-name></realm-name> </login-config> <!--聲明角色--> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> </web-app>
2.6、創建用戶及角色
在 Keycloak 中創建如下用戶和角色:
用戶 | 擁有角色 |
admin | admin |
test-user | user |
2.7、測試應用
把 web 應用部署到 Tomcat 中並訪問應用:http://localhost:8080/keycloak-tomcat,此時頁面會跳轉到 Keycloak 的登錄頁面:
輸入用戶名密碼后會跳轉到自己應用的頁面。使用 admin 用戶登錄后,可以訪問應用的所有 url;如果使用 test-user 用戶登錄並訪問 http://localhost:8080/keycloak-tomcat/admin/index.jsp 會報 403 錯誤:
2.8、登出
這里為了方便使用一個 jsp(/logout.jsp) 來執行登出並跳到應用首頁:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <% request.logout(); response.sendRedirect(request.getContextPath()); %>
訪問 http://localhost:8080/keycloak-tomcat/logout.jsp,由於登出后會跳到應用的首頁,首頁屬於被保護的資源,所以又會跳轉到 Keycloak 的登錄頁面。
Tomcat 整合 Keycloak 的詳細說明可參考官網:https://www.keycloak.org/docs/16.1/securing_apps/index.html#_spring_boot_adapter。
3、SpringBoot 中使用 Keycloak
3.1、引入依賴
<dependencyManagement> <dependencies> <dependency> <groupId>org.keycloak.bom</groupId> <artifactId>keycloak-adapter-bom</artifactId> <version>16.1.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> ... <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> </dependency> </dependencies>
3.2、創建客戶端
假設 SpringBoot 應用的 context-path 為 /,在 Keycloak 中創建客戶端:client-springboot
3.3、創建用戶及角色
在 Keycloak 中創建如下用戶和角色:
用戶 | 擁有角色 |
admin | admin |
test-user | user |
3.4、定義資源保護規則(application.yml)
keycloak: public-client: true auth-server-url: http://10.49.196.10:8080/auth realm: master resource: client-springboot security-constraints: - #登出 url 不需要權限控制 securityCollections: - name: logout patterns: - /logout - #管理員相關資源保護, admin 角色的用戶可以訪問 authRoles: - admin securityCollections: - name: admin patterns: - /admin/* - /system/* - #用戶相關資源保護, user 或 admin 角色的用戶可以訪問 authRoles: - admin - user securityCollections: - name: user patterns: - /*
3.5、編寫測試 Controller
package com.abc.demo.keycloak.controller; import org.keycloak.KeycloakPrincipal; import org.keycloak.representations.AccessToken; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.Principal; import java.util.Set; @RequestMapping("/*") @RestController public class TestController { @ResponseBody @RequestMapping("/") public String welcome() { return "welcome"; } @ResponseBody @RequestMapping("/admin/index") public String adminIndex() { return "admin index"; } @ResponseBody @RequestMapping("/user/index") public String userIndex() { return "user index"; } /** * 獲取用戶信息 * @param principal * @return */ @ResponseBody @RequestMapping("/getUserInfo") public String getUserInfo(Principal principal) { String result = ""; if (principal instanceof KeycloakPrincipal) { AccessToken accessToken = ((KeycloakPrincipal) principal).getKeycloakSecurityContext().getToken(); String preferredUsername = accessToken.getPreferredUsername(); AccessToken.Access realmAccess = accessToken.getRealmAccess(); Set<String> roles = realmAccess.getRoles(); result = "當前登錄用戶:" + preferredUsername + ", 角色:" + roles; } return result; } /** * 登出 * @param request * @param response * @throws ServletException * @throws IOException */ @RequestMapping("/logout") public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.logout(); response.sendRedirect(request.getContextPath()); } }
3.6、測試應用
訪問應用:http://localhost:8080,此時頁面會跳轉到 Keycloak 的登錄頁面:
輸入用戶名密碼后會跳轉到自己應用的 url。使用 admin 用戶登錄后,可以訪問應用的所有 url;如果使用 test-user 用戶登錄並訪問 http://localhost:8080/admin/index 會報 403 錯誤:
3.7、登出
訪問登出 url:http://localhost:8080/logout,由於登出后會跳到應用的首頁,首頁屬於被保護的資源,所以又會跳轉到 Keycloak 的登錄頁面。
SpringBoot 整合 Keycloak 的詳細說明可參考官網:https://www.keycloak.org/docs/16.1/securing_apps/index.html#_spring_boot_adapter。
4、可能出現問題
4.1、Token is not active
在單點登錄的時候應用可能會報如下錯誤:
org.keycloak.adapters.OAuthRequestAuthenticator.resolveCode failed verification of token: Token is not active
這是由於 Keycloak 運行的機器和應用運行的機器時間相差較大導致的,處理方法:修改時間錯誤的機器上時間,使各機器上的時間一致。