Keycloak được nhúng trong ứng dụng khởi động mùa xuân

1. Khái quát chung

Keycloak là một giải pháp Quản lý Danh tính và Truy cập mã nguồn mở do RedHat quản lý và được phát triển bằng Java bởi JBoss.

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách thiết lập máy chủ Keycloak được nhúng trong ứng dụng Spring Boot . Điều này giúp bạn dễ dàng khởi động máy chủ Keycloak được cấu hình sẵn.

Keycloak cũng có thể được chạy như một máy chủ độc lập, nhưng sau đó nó liên quan đến việc tải xuống và thiết lập thông qua Bảng điều khiển dành cho quản trị viên.

2. Cấu hình trước của Keycloak

Để bắt đầu, hãy hiểu cách chúng ta có thể định cấu hình trước máy chủ Keycloak.

Máy chủ chứa một tập hợp các cảnh giới, với mỗi cảnh giới hoạt động như một đơn vị biệt lập để quản lý người dùng. Để cấu hình trước nó, chúng ta cần chỉ định tệp định nghĩa lĩnh vực ở định dạng JSON.

Mọi thứ có thể được định cấu hình bằng Bảng điều khiển dành cho quản trị viên Keycloak vẫn tồn tại trong JSON này.

Máy chủ Ủy quyền của chúng tôi sẽ được định cấu hình trước với baeldung-domains.json . Hãy xem một vài cấu hình có liên quan trong tệp:

  • người dùng : người dùng mặc định của chúng tôi sẽ được [bảo vệ email][bảo vệ email] ; họ cũng sẽ có thông tin đăng nhập của họ ở đây
  • khách hàng : chúng tôi sẽ xác định một ứng dụng khách với id newClient
  • standardFlowEnabled : đặt thành true để kích hoạt Luồng mã ủy quyền cho newClient
  • redirectUris : newClient của các URL mà máy chủ sẽ chuyển hướng đến sau khi xác thực thành công được liệt kê ở đây
  • webOrigins : đặt thành “+” để cho phép hỗ trợ CORS cho tất cả các URL được liệt kê là redirectUris

Máy chủ Keycloak phát hành mã thông báo JWT theo mặc định, vì vậy không cần cấu hình riêng cho việc đó. Tiếp theo hãy xem các cấu hình của Maven.

3. Cấu hình Maven

Vì chúng tôi sẽ nhúng Keycloak vào bên trong ứng dụng Spring Boot nên không cần tải xuống riêng.

Thay vào đó, chúng tôi sẽ thiết lập nhóm phụ thuộc sau:

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-actuator   org.springframework.boot spring-boot-starter-data-jpa   com.h2database h2 runtime  

Lưu ý rằng chúng tôi đang sử dụng phiên bản 2.2.6.RELEASE của Spring Boot tại đây. Các phụ thuộc spring-boot-starter-data-jpa và H2 đã được thêm vào để duy trì sự bền bỉ. Các phần phụ thuộc springframework.boot khác là để hỗ trợ web, vì chúng tôi cũng cần có thể chạy máy chủ ủy quyền Keycloak cũng như bảng điều khiển quản trị như các dịch vụ web.

Chúng tôi cũng sẽ cần một số phụ thuộc cho Keycloak và RESTEasy :

 org.jboss.resteasy resteasy-jackson2-provider 3.12.1.Final   org.keycloak keycloak-dependencies-server-all 11.0.2 pom  

Kiểm tra trang web Maven để biết các phiên bản mới nhất của Keycloak và RESTEasy.

Và cuối cùng, chúng ta phải ghi đè , để sử dụng phiên bản do Keycloak khai báo thay vì phiên bản được xác định bởi Spring Boot:

 10.1.8.Final 

4. Cấu hình Keycloak Nhúng

Bây giờ hãy xác định cấu hình Spring cho máy chủ ủy quyền của chúng tôi:

@Configuration public class EmbeddedKeycloakConfig { @Bean ServletRegistrationBean keycloakJaxRsApplication( KeycloakServerProperties keycloakServerProperties, DataSource dataSource) throws Exception { mockJndiEnvironment(dataSource); EmbeddedKeycloakApplication.keycloakServerProperties = keycloakServerProperties; ServletRegistrationBean servlet = new ServletRegistrationBean( new HttpServlet30Dispatcher()); servlet.addInitParameter("javax.ws.rs.Application", EmbeddedKeycloakApplication.class.getName()); servlet.addInitParameter(ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, keycloakServerProperties.getContextPath()); servlet.addInitParameter(ResteasyContextParameters.RESTEASY_USE_CONTAINER_FORM_PARAMS, "true"); servlet.addUrlMappings(keycloakServerProperties.getContextPath() + "/*"); servlet.setLoadOnStartup(1); servlet.setAsyncSupported(true); return servlet; } @Bean FilterRegistrationBean keycloakSessionManagement( KeycloakServerProperties keycloakServerProperties) { FilterRegistrationBean filter = new FilterRegistrationBean(); filter.setName("Keycloak Session Management"); filter.setFilter(new EmbeddedKeycloakRequestFilter()); filter.addUrlPatterns(keycloakServerProperties.getContextPath() + "/*"); return filter; } private void mockJndiEnvironment(DataSource dataSource) throws NamingException { NamingManager.setInitialContextFactoryBuilder( (env) -> (environment) -> new InitialContext() { @Override public Object lookup(Name name) { return lookup(name.toString()); } @Override public Object lookup(String name) { if ("spring/datasource".equals(name)) { return dataSource; } return null; } @Override public NameParser getNameParser(String name) { return CompositeName::new; } @Override public void close() { } }); } } 

Lưu ý: đừng lo lắng về lỗi biên dịch, chúng ta sẽ định nghĩa lớp EmbeddedKeycloakRequestFilter sau này.

Như chúng ta có thể thấy ở đây, trước tiên chúng tôi đã định cấu hình Keycloak như một ứng dụng JAX-RS với KeycloakServerProperties để lưu trữ liên tục các thuộc tính Keycloak như được chỉ định trong tệp định nghĩa vùng của chúng tôi. Sau đó, chúng tôi đã thêm bộ lọc quản lý phiên và mô phỏng môi trường JNDI để sử dụng spring / datasource , là cơ sở dữ liệu H2 trong bộ nhớ của chúng tôi.

5. KeycloakServerProperties

Bây giờ chúng ta hãy xem xét KeycloakServerProperties mà chúng tôi vừa đề cập:

@ConfigurationProperties(prefix = "keycloak.server") public class KeycloakServerProperties { String contextPath = "/auth"; String realmImportFile = "baeldung-realm.json"; AdminUser adminUser = new AdminUser(); // getters and setters public static class AdminUser { String username = "admin"; String password = "admin"; // getters and setters } } 

Như chúng ta thấy, đây là một POJO đơn giản để thiết lập contextPath , adminUser và tệp định nghĩa vùng .

6. EmbeddedKeycloakApplication

Tiếp theo, hãy xem lớp, sử dụng các cấu hình mà chúng tôi đã đặt trước đó, để tạo các cảnh giới:

public class EmbeddedKeycloakApplication extends KeycloakApplication { private static final Logger LOG = LoggerFactory.getLogger(EmbeddedKeycloakApplication.class); static KeycloakServerProperties keycloakServerProperties; protected void loadConfig() { JsonConfigProviderFactory factory = new RegularJsonConfigProviderFactory(); Config.init(factory.create() .orElseThrow(() -> new NoSuchElementException("No value present"))); } public EmbeddedKeycloakApplication() { createMasterRealmAdminUser(); createBaeldungRealm(); } private void createMasterRealmAdminUser() { KeycloakSession session = getSessionFactory().create(); ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session); AdminUser admin = keycloakServerProperties.getAdminUser(); try { session.getTransactionManager().begin(); applianceBootstrap.createMasterRealmUser(admin.getUsername(), admin.getPassword()); session.getTransactionManager().commit(); } catch (Exception ex) { LOG.warn("Couldn't create keycloak master admin user: {}", ex.getMessage()); session.getTransactionManager().rollback(); } session.close(); } private void createBaeldungRealm() { KeycloakSession session = getSessionFactory().create(); try { session.getTransactionManager().begin(); RealmManager manager = new RealmManager(session); Resource lessonRealmImportFile = new ClassPathResource( keycloakServerProperties.getRealmImportFile()); manager.importRealm(JsonSerialization.readValue(lessonRealmImportFile.getInputStream(), RealmRepresentation.class)); session.getTransactionManager().commit(); } catch (Exception ex) { LOG.warn("Failed to import Realm json file: {}", ex.getMessage()); session.getTransactionManager().rollback(); } session.close(); } } 

7. Triển khai nền tảng tùy chỉnh

Như chúng tôi đã nói, Keycloak được phát triển bởi RedHat / JBoss. Do đó, nó cung cấp chức năng và các thư viện mở rộng để triển khai ứng dụng trên máy chủ Wildfly hoặc dưới dạng giải pháp Quarkus.

Trong trường hợp này, chúng tôi đang loại bỏ các lựa chọn thay thế đó và do đó, chúng tôi phải cung cấp các triển khai tùy chỉnh cho một số giao diện và lớp dành riêng cho nền tảng.

Ví dụ, trong EmbeddedKeycloakApplication, chúng tôi vừa định cấu hình, lần đầu tiên chúng tôi tải cấu hình máy chủ của Keycloak-server.json , sử dụng một lớp con trống của trừu tượng JsonConfigProviderFactory :

public class RegularJsonConfigProviderFactory extends JsonConfigProviderFactory { }

Sau đó, chúng tôi mở rộng KeycloakApplication để tạo ra hai lĩnh vực: chủbaeldung . Chúng được tạo theo các thuộc tính được chỉ định trong tệp định nghĩa lĩnh vực của chúng tôi, baeldung-domains.json .

Như bạn có thể thấy, chúng tôi sử dụng KeycloakSession để thực hiện tất cả các giao dịch và để điều này hoạt động bình thường, chúng tôi phải tạo một AbstractRequestFilter tùy chỉnh ( EmbeddedKeycloakRequestFilter ) và thiết lập bean cho việc này bằng cách sử dụng KeycloakSessionServletFilter trong tệp EmbeddedKeycloakConfig .

Ngoài ra, chúng tôi cần một số nhà cung cấp tùy chỉnh để chúng tôi có triển khai org.keycloak.common.util.ResteasyProviderorg.keycloak.platform.PlatformProvider của riêng mình và không dựa vào các phụ thuộc bên ngoài.

Quan trọng là, thông tin về các nhà cung cấp tùy chỉnh này nên được đưa vào thư mục META-INF / services của dự án để chúng được thu thập trong thời gian chạy.

8. Mang tất cả lại với nhau

Như chúng ta đã thấy, Keycloak đã đơn giản hóa nhiều cấu hình yêu cầu từ phía ứng dụng . Không cần phải xác định theo chương trình nguồn dữ liệu hoặc bất kỳ cấu hình bảo mật nào.

Để kết hợp tất cả lại với nhau, chúng ta cần xác định cấu hình cho Spring và ứng dụng Spring Boot.

8.1. application.yml

Chúng tôi sẽ sử dụng một YAML đơn giản cho các cấu hình Spring:

server: port: 8083 spring: datasource: username: sa url: jdbc:h2:mem:testdb keycloak: server: contextPath: /auth adminUser: username: bael-admin password: ******** realmImportFile: baeldung-realm.json

8.2. Ứng dụng khởi động mùa xuân

Cuối cùng, đây là Ứng dụng khởi động mùa xuân:

@SpringBootApplication(exclude = LiquibaseAutoConfiguration.class) @EnableConfigurationProperties(KeycloakServerProperties.class) public class AuthorizationServerApp { private static final Logger LOG = LoggerFactory.getLogger(AuthorizationServerApp.class); public static void main(String[] args) throws Exception { SpringApplication.run(AuthorizationServerApp.class, args); } @Bean ApplicationListener onApplicationReadyEventListener( ServerProperties serverProperties, KeycloakServerProperties keycloakServerProperties) { return (evt) -> { Integer port = serverProperties.getPort(); String keycloakContextPath = keycloakServerProperties.getContextPath(); LOG.info("Embedded Keycloak started: //localhost:{}{} to use keycloak", port, keycloakContextPath); }; } }

Đáng chú ý, ở đây chúng tôi đã kích hoạt cấu hình KeycloakServerProperties để đưa nó vào bean ApplicationListener .

Sau khi chạy lớp này, chúng tôi có thể truy cập trang chào mừng của máy chủ ủy quyền tại // localhost: 8083 / auth / .

9. Kết luận

Trong hướng dẫn nhanh này, chúng ta đã biết cách thiết lập máy chủ Keycloak được nhúng trong ứng dụng Spring Boot. Mã nguồn cho ứng dụng này có sẵn trên GitHub.

Ý tưởng ban đầu cho việc triển khai này được phát triển bởi Thomas Darimont và có thể được tìm thấy trong dự án nhúng-spring-boot-keycloak-server.