{"id":320859,"date":"2021-04-05T09:00:44","date_gmt":"2021-04-05T09:00:44","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=320859"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2021-04-05T09:00:44","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=320859","title":{"rendered":"\u0421\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043b\u044f Keycloak"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u044f Keycloak \u0432 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u0432\u044b\u0441\u043e\u043a\u0430 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0434\u0440\u0435\u0432\u043d\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0433\u0434\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u0447\u0443\u0434\u043b\u0438\u0432\u043e\u043c \u0432\u0438\u0434\u0435. \u0422\u0430\u043a\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (User Federation Provider \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u043b\u043e\u0433\u0438\u0438 Keycloak). \u041d\u0438\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043a\u0440\u0430\u0442\u043a\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/p>\n<hr>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0434\u0440\u0443\u0433 \u0432\u044b \u043d\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 Keycloak, \u0442\u043e \u0432\u043e\u0442 \u0432\u0430\u043c \u0446\u0438\u0442\u0430\u0442\u0430 \u0438\u0437 Wikipedia:<\/p>\n<blockquote>\n<p><strong>Keycloak<\/strong>&nbsp;\u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438&nbsp;single sign-on&nbsp;\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c, \u043d\u0430\u0446\u0435\u043b\u0435\u043d \u043d\u0430 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b.<\/p>\n<\/blockquote>\n<p>\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u043c\u0438\u0440\u0435 Keycloak \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e \u043a\u0430\u043a \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 OAuth 2.0, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.<\/p>\n<p>\u0412 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u043f\u043b\u0430\u043d\u0435 Keycloak &#8212; \u044d\u0442\u043e web-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 WildFly, \u0447\u0442\u043e \u0443 \u043a\u043e\u0433\u043e-\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0443\u0440\u0430\u0448\u043a\u0438 \u043f\u043e \u0442\u0435\u043b\u0443 \u043e\u0442 \u0432\u043e\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0439 \u043e \u043a\u0440\u043e\u0432\u0430\u0432\u043e\u043c \u044d\u043d\u0442\u0435\u0440\u043f\u0440\u0430\u0439\u0437\u0435. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0435\u043e\u0440\u0438\u0438, \u043f\u043e\u0440\u0430 \u0437\u0430\u0441\u0443\u0447\u0438\u0442\u044c \u0440\u0443\u043a\u0430\u0432\u0430!<\/p>\n<p>\u041d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f Keycloak \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432 WAR. \u0414\u043b\u044f \u0435\u0433\u043e \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e Java 8. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0441\u0431\u043e\u0440\u043a\u0438 \u0432\u043e\u0437\u044c\u043c\u0451\u043c Gradle, \u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u0443\u043a\u0430\u0436\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0438:<\/p>\n<pre><code>compileOnly \"org.keycloak:keycloak-core:12.0.3\" compileOnly \"org.keycloak:keycloak-server-spi:12.0.3\" compileOnly \"org.jboss.logging:jboss-logging:3.4.1.Final\"  implementation \"org.springframework:spring-core:5.3.3\" implementation \"org.springframework:spring-jdbc:5.3.3\" implementation \"org.springframework.security:spring-security-core:5.4.4\"  testImplementation platform('org.junit:junit-bom:5.7.1') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.mockito:mockito-core:3.7.7'  testRuntimeOnly 'javax.ws.rs:javax.ws.rs-api:2.1.1' testRuntimeOnly 'com.h2database:h2:1.4.200'<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0443\u043b\u0438 Spring Framework \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043b\u0438\u0448\u043d\u0438\u043c\u0438, \u0442\u0435\u043c \u0431\u043e\u043b\u0435\u0435, \u0447\u0442\u043e \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 Spring. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u044f \u0438\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u0435\u0439.<\/p>\n<p>\u0421\u0430\u043c\u044b\u0435 \u0432\u0430\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 &#8212; \u044d\u0442\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 Keycloak. \u0412 \u0432\u043e\u043f\u0440\u043e\u0441\u0435 \u043d\u043e\u043c\u0435\u0440\u043e\u0432 \u0432\u0435\u0440\u0441\u0438\u0439 \u044d\u0442\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u0430\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u0432\u0435\u0440\u0441\u0438\u044f Keycloak \u0443 \u0432\u0430\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f.<\/p>\n<p>\u0414\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d JBoss Logging, \u043a\u0430\u043a &#171;\u0440\u043e\u0434\u043d\u043e\u0435&#187; \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 WildFly. \u0414\u043e\u043f\u0443\u0441\u043a\u0430\u044e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u043b\u043e\u0433, \u043f\u043e\u043b\u044c\u0437\u0443\u044f\u0441\u044c \u0447\u0435\u043c-\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c, \u0432\u0440\u043e\u0434\u0435 SLF4J.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c <code>io.freefair.lombok<\/code>.<\/p>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 Keycloak \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c <code>org.keycloak.storage.UserStorageProvider<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0422\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0432 \u0442\u0430\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u0445, \u043a\u0430\u043a <code>org.keycloak.storage.user.UserLookupProvider<\/code> \u0438 <code>org.keycloak.credential.CredentialInputValidator<\/code>. \u041f\u0435\u0440\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0438\u043c\u0435\u043d\u0438, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441\u0443 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b. \u0412\u0442\u043e\u0440\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 &#8212; \u043c\u0435\u0442\u043e\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f. \u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.UserModel<\/code> (\u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0451 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439). \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 <code>org.keycloak.storage.adapter.AbstractUserAdapter<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.UserModel<\/code>:<\/p>\n<pre><code class=\"java\">public class LegacyDatabaseUserModel extends AbstractUserAdapter {     public static final String ATTRIBUTE_PASSWORD = \"password\";        private final MultivaluedHashMap&lt;String, String&gt; attributes = new MultivaluedHashMap&lt;&gt;();     private final Set&lt;RoleModel&gt; roles;      private LegacyDatabaseUserModel(Builder builder) {         super(builder.session, builder.realm, builder.storageProviderModel);         this.attributes.putSingle(UserModel.USERNAME, builder.username);         this.attributes.putSingle(UserModel.FIRST_NAME, builder.firstName);         this.attributes.putSingle(UserModel.LAST_NAME, builder.lastName);         this.attributes.putSingle(ATTRIBUTE_PASSWORD, builder.password);         this.roles = Collections.unmodifiableSet(builder.roles);     }      public static Builder builder() {         return new Builder();     }        @Override     public String getUsername() {         return getFirstAttribute(UserModel.USERNAME);     }      @Override     public String getFirstName() {         return getFirstAttribute(UserModel.FIRST_NAME);     }      @Override     public String getLastName() {         return getFirstAttribute(UserModel.LAST_NAME);     }        @Override     public Map&lt;String, List&lt;String&gt;&gt; getAttributes() {         return new MultivaluedHashMap&lt;&gt;(attributes);     }      @Override     public String getFirstAttribute(String name) {         return attributes.getFirst(name);     }      @Override     public List&lt;String&gt; getAttribute(String name) {         return attributes.get(name);     }      @Override     protected Set&lt;RoleModel&gt; getRoleMappingsInternal() {         return roles;     }      public static class Builder {         ...     } }<\/code><\/pre>\n<p>\u041d\u0430\u0431\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0432\u0435\u043b\u0438\u043a, \u0430 \u0441\u0430\u043c\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b, \u0447\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u043a\u043b\u0430\u0441\u0441\u043e\u043c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u044b-\u043c\u0443\u0442\u0430\u0442\u043e\u0440\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0440\u043e\u0441\u0430\u044e\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. \u041f\u043e\u0447\u0435\u043c\u0443 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u0432 <code>Map<\/code>, \u0430 \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430, \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 &#8212; \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.RoleModel<\/code> \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0414\u043b\u044f \u043d\u0435\u0451 \u044f \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0441\u0435\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"java\">@AllArgsConstructor public class LegacyDatabaseRoleModel implements RoleModel {     @Getter     private final RoleContainerModel container;     @Getter     private final String name;      @Override     public String getId() {         return getName();     }      @Override     public void setName(String name) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public String getDescription() {         return null;     }      @Override     public void setDescription(String description) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public boolean isComposite() {         return false;     }      @Override     public void addCompositeRole(RoleModel role) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void removeCompositeRole(RoleModel role) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public Stream&lt;RoleModel&gt; getCompositesStream() {         return Stream.empty();     }      @Override     public boolean isClientRole() {         return false;     }      @Override     public String getContainerId() {         return container.getId();     }      @Override     public boolean hasRole(RoleModel role) {         return false;     }      @Override     public Map&lt;String, List&lt;String&gt;&gt; getAttributes() {         return Collections.emptyMap();     }      @Override     public void setSingleAttribute(String name, String value) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void setAttribute(String name, List&lt;String&gt; values) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void removeAttribute(String name) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public Stream&lt;String&gt; getAttributeStream(String name) {         return Stream.empty();     } } <\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u0440\u043e\u043b\u0438 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u043b\u0438\u0448\u044c \u0438\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0440\u043e\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u0430\u043a\u0436\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0435\u0451 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u043e\u043c \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043c\u0435\u0442\u043e\u0434\u044b-\u043c\u0443\u0442\u0430\u0442\u043e\u0440\u044b \u0431\u0440\u043e\u0441\u0430\u044e\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0431\u0443\u0434\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u043e \u0435\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443. \u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c. \u041c\u0435\u0442\u043e\u0434 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"java\">private final ConcurrentMap&lt;UserModelKey, LegacyDatabaseUserModel&gt; loadedUsers = new ConcurrentHashMap&lt;&gt;();  @Override public LegacyDatabaseUserModel getUserByUsername(String username, RealmModel realm) {     UserModelKey userKey = new UserModelKey(username, realm.getId());     return loadedUsers.computeIfAbsent(userKey, k -&gt; {         LegacyDatabaseUserModel user = findUserByName(username, realm);         if (user != null) {             log.debugv(\"User is loaded by name \\\"{0}\\\"\", username);         }         return user;     }); }<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0435\u0437\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Keycloak \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430. \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0451\u043d \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441 <code>java.util.concurrent.ConcurrentMap<\/code>. \u041c\u0435\u0442\u043e\u0434 <code>findUserByName<\/code> \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>org.springframework.jdbc.core.JdbcTemplate<\/code>&nbsp;\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <code>org.springframework.jdbc.core.ResultSetExtractor<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u043f\u043e\u043b\u043e\u0447\u043a\u0430\u043c, \u043f\u043e\u043f\u0443\u0442\u043d\u043e \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044f \u043d\u0430\u0431\u043e\u0440 \u0435\u0433\u043e \u0440\u043e\u043b\u0435\u0439.<\/p>\n<pre><code class=\"java\">private LegacyDatabaseUserModel findUserByName(String username, RealmModel realm) { \treturn jdbcTemplate.query(SQL_FIND_USER_BY_NAME, new Object[]{username}, new int[]{Types.VARCHAR}, \t\t\t\tnew LegacyDatabaseUserModelResultSetExtractor(realm)); }<\/code><\/pre>\n<pre><code class=\"java\">@RequiredArgsConstructor private class LegacyDatabaseUserModelResultSetExtractor implements ResultSetExtractor&lt;LegacyDatabaseUserModel&gt; {     final RealmModel realm;      @Override     public LegacyDatabaseUserModel extractData(ResultSet rs) throws SQLException, DataAccessException {         if (!rs.next()) {             return null;         }          LegacyDatabaseUserModel.Builder userModelBuilder = LegacyDatabaseUserModel.builder()                 .session(session)                 .realm(realm)                 .storageProviderModel(storageProviderModel)                 .username(rs.getString(1))                 .password(rs.getString(2))                 .firstName(rs.getString(3))                 .lastName(rs.getString(4))                 .withRole(new LegacyDatabaseRoleModel(realm, rs.getString(5)));          while (rs.next()) {             userModelBuilder.withRole(new LegacyDatabaseRoleModel(realm, rs.getString(5)));         }          return userModelBuilder.build();     } }<\/code><\/pre>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u043f\u043e \u0435\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u043d\u0435 \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u041f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043b\u0438\u0448\u044c \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430:<\/p>\n<pre><code class=\"java\">@Override public LegacyDatabaseUserModel getUserById(String id, RealmModel realm) { \tStorageId storageId = new StorageId(id); \tString username = storageId.getExternalId(); \treturn getUserByUsername(username, realm); }<\/code><\/pre>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0438\u043c\u0435\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0432\u0438\u0434: <code>f:&lt;storageProvideId&gt;:&lt;username&gt;<\/code>. \u0414\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u043c <code>org.keycloak.storage.StorageId<\/code>.<\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u0435\u0442\u043e\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f:<\/p>\n<pre><code class=\"java\">@Override public boolean isValid(RealmModel realm, UserModel userModel, CredentialInput credentialInput) {     if (!supportsCredentialType(credentialInput.getType())) {         log.debugv(\"Credential type \\\"{0}\\\" is not supported\", credentialInput.getType());         return false;     }      String password = user.getFirstAttribute(LegacyDatabaseUserModel.ATTRIBUTE_PASSWORD);     return passwordEncoder.matches(credentialInput.getChallengeResponse(), password); }<\/code><\/pre>\n<p>\u0412 \u043d\u0451\u043c \u043c\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442, \u0430 \u0433\u043e\u0442\u043e\u0432\u044b \u043e\u0442\u043a\u043b\u0438\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e \u043f\u0430\u0440\u043e\u043b\u044e (\u0441\u0440\u0435\u0434\u0438 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u043e\u043c\u0443 \u043a\u043e\u0434\u0443). \u0417\u0430\u0442\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u0430\u0440\u043e\u043b\u044c \u0438\u0437 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412\u043e\u0442 \u0442\u0443\u0442-\u0442\u043e \u043d\u0430\u043c \u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 <code>Map<\/code>. \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>org.keycloak.models.UserModel<\/code> \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430\u0434\u0435\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c &#171;\u0441\u043a\u0430\u0441\u0442\u0438\u0442\u044c&#187; \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043c\u0435\u0442\u043e\u0434\u0430 <code>isValid<\/code> \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043a\u043b\u0430\u0441\u0441\u0443 <code>com.habr.keycloak.model.LegacyDatabaseUserModel<\/code> &#8212; \u0437\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c \u043c\u043e\u0436\u0435\u0442 \u0441\u043a\u0440\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0438\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f. \u0421\u0430\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.springframework.security.crypto.password.PasswordEncoder<\/code>.<\/p>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b \u0432 Keycloak \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0441\u0430\u043c\u0438 \u0441\u043e\u0431\u043e\u0439. \u0418\u043c \u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0444\u0430\u0431\u0440\u0438\u043a\u0438. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.storage.UserStorageProviderFactory<\/code>. \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0435\u0439 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043f\u0430\u0440\u043e\u043b\u0435\u0439.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"java\">@Override public void init(Config.Scope config) {     initDataSource();     initPasswordEncoder(); }<\/code><\/pre>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u044b\u0432\u0430\u0442\u044c \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432:<\/p>\n<pre><code class=\"java\">private PropertySource&lt;Map&lt;String, Object&gt;&gt; getPropertySource() {     if (propertySource == null) {         propertySource = getDefaultPropertySource();     }     return propertySource; }  private PropertySource&lt;Map&lt;String, Object&gt;&gt; getDefaultPropertySource() {     return new PropertiesPropertySource(\"default\", System.getProperties()); }<\/code><\/pre>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0439\u0442\u0438 \u043f\u043e Keycloak-way \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 <code>*.properties<\/code>, \u043f\u0443\u0442\u044c \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0437\u0430\u0434\u0430\u043d \u0432 <code>standalone.xml<\/code>:<\/p>\n<pre><code class=\"java\">@Override public void init(Config.Scope config) {     String propertyFilePath = config.get(\"property-file-path\");     ...<\/code><\/pre>\n<p>\u041d\u043e \u044d\u0442\u043e \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u0443\u0436\u0435 \u0432\u043a\u0443\u0441\u043e\u0432\u0449\u0438\u043d\u0430.<\/p>\n<p>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u0431\u0435\u0437 \u043f\u0443\u043b\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439:<\/p>\n<pre><code class=\"java\">private void initDataSource() {     String driverClassName = getDataSourceDriverClassName();     String url = getDataSourceUrl();      SimpleDriverDataSource dataSource = new SimpleDriverDataSource();     try {         dataSource.setDriverClass((Class&lt;? extends Driver&gt;) Class.forName(driverClassName));         dataSource.setUrl(url);         dataSource.setUsername(getDataSourceUsername());         dataSource.setPassword(getDataSourcePassword());         this.dataSource = dataSource;         log.debugv(\"Data source to connect with database \\\"{0}\\\" is created\", url);     } catch (ClassNotFoundException e) {         throw new IllegalStateException(\"JDBC driver class \\\"\" + driverClassName + \"\\\" is not found\", e);     } }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0441\u0442\u043e\u0438\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0436\u043d\u043e\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e \u0432 WAR-\u0444\u0430\u0439\u043b\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u043b\u0438\u0431\u043e \u0432 \u043c\u043e\u0434\u0443\u043b\u044f\u0445 Keycloak. \u0417\u0434\u0435\u0441\u044c \u044f \u043f\u043e\u0448\u0451\u043b \u043f\u043e \u0432\u0442\u043e\u0440\u043e\u043c\u0443 \u043f\u0443\u0442\u0438, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u044f\u0432\u043d\u043e \u0434\u0435\u043a\u043b\u0430\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u0435\u0433\u043e \u043b\u0435\u0433\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c. \u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d \u0432 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Keycloak.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u0435\u0439 \u044f \u0432\u0437\u044f\u043b <code>org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder<\/code>.<\/p>\n<p>\u0424\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0448\u0430\u0433 &#8212; \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u043f\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430\u043c Factory Finder&#8217;\u0430, \u0447\u0442\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 <code>META-INF\/services<\/code> \u0444\u0430\u0439\u043b\u0430 <code>org.keycloak.storage.UserStorageProviderFactory<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0433\u043e \u0438\u043c\u044f \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0430\u0448\u0435\u0439 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432.<\/p>\n<p>\u041a\u0430\u043a \u0431\u044b\u043b\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0440\u0430\u043d\u0435\u0435, \u043f\u043b\u0430\u0433\u0438\u043d \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f Keycloak. \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u043a\u0430\u0437\u0430\u0442\u044c Keycloak, \u0447\u0442\u043e \u043c\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b <code>jboss-deployment-structure.xml<\/code> \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 <code>META-INF<\/code>:<\/p>\n<pre><code class=\"xml\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; &lt;jboss-deployment-structure&gt;     &lt;deployment&gt;         &lt;dependencies&gt;             &lt;module name=\"org.postgresql\"\/&gt;         &lt;\/dependencies&gt;     &lt;\/deployment&gt; &lt;\/jboss-deployment-structure&gt; <\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b Keycloak \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u0438\u043b \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d, \u0435\u0433\u043e (\u043f\u043b\u0430\u0433\u0438\u043d) \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <code>$KEYCLOAK_HOME\/standalone\/deployments<\/code>. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u043f\u0435\u0445\u0430 \u0441 \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435 Keycloak \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 <em>User Federation<\/em> \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c <code>habr.legacy-database<\/code>, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u043a \u0432\u044b\u0434\u0430\u0447\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432.<\/p>\n<p><a href=\"https:\/\/github.com\/rchigvintsev\/keycloak-user-storage-provider-demo\" rel=\"noopener noreferrer nofollow\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043d\u0430 GitHub<\/a>.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/550704\/\"> https:\/\/habr.com\/ru\/post\/550704\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u044f Keycloak \u0432 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u0432\u044b\u0441\u043e\u043a\u0430 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0434\u0440\u0435\u0432\u043d\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0433\u0434\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u0447\u0443\u0434\u043b\u0438\u0432\u043e\u043c \u0432\u0438\u0434\u0435. \u0422\u0430\u043a\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (User Federation Provider \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u043b\u043e\u0433\u0438\u0438 Keycloak). \u041d\u0438\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043a\u0440\u0430\u0442\u043a\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/p>\n<hr>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0434\u0440\u0443\u0433 \u0432\u044b \u043d\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 Keycloak, \u0442\u043e \u0432\u043e\u0442 \u0432\u0430\u043c \u0446\u0438\u0442\u0430\u0442\u0430 \u0438\u0437 Wikipedia:<\/p>\n<blockquote>\n<p><strong>Keycloak<\/strong>&nbsp;\u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438&nbsp;single sign-on&nbsp;\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c, \u043d\u0430\u0446\u0435\u043b\u0435\u043d \u043d\u0430 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b.<\/p>\n<\/blockquote>\n<p>\u0412 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u043c\u0438\u0440\u0435 Keycloak \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0435\u043d \u043f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e \u043a\u0430\u043a \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 OAuth 2.0, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0442\u0435\u043c \u0438\u043b\u0438 \u0438\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.<\/p>\n<p>\u0412 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u043f\u043b\u0430\u043d\u0435 Keycloak &#8212; \u044d\u0442\u043e web-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 WildFly, \u0447\u0442\u043e \u0443 \u043a\u043e\u0433\u043e-\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0443\u0440\u0430\u0448\u043a\u0438 \u043f\u043e \u0442\u0435\u043b\u0443 \u043e\u0442 \u0432\u043e\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0439 \u043e \u043a\u0440\u043e\u0432\u0430\u0432\u043e\u043c \u044d\u043d\u0442\u0435\u0440\u043f\u0440\u0430\u0439\u0437\u0435. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0435\u043e\u0440\u0438\u0438, \u043f\u043e\u0440\u0430 \u0437\u0430\u0441\u0443\u0447\u0438\u0442\u044c \u0440\u0443\u043a\u0430\u0432\u0430!<\/p>\n<p>\u041d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f Keycloak \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0432 WAR. \u0414\u043b\u044f \u0435\u0433\u043e \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e Java 8. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0441\u0431\u043e\u0440\u043a\u0438 \u0432\u043e\u0437\u044c\u043c\u0451\u043c Gradle, \u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u0445 \u0443\u043a\u0430\u0436\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0438:<\/p>\n<pre><code>compileOnly \"org.keycloak:keycloak-core:12.0.3\" compileOnly \"org.keycloak:keycloak-server-spi:12.0.3\" compileOnly \"org.jboss.logging:jboss-logging:3.4.1.Final\"  implementation \"org.springframework:spring-core:5.3.3\" implementation \"org.springframework:spring-jdbc:5.3.3\" implementation \"org.springframework.security:spring-security-core:5.4.4\"  testImplementation platform('org.junit:junit-bom:5.7.1') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.mockito:mockito-core:3.7.7'  testRuntimeOnly 'javax.ws.rs:javax.ws.rs-api:2.1.1' testRuntimeOnly 'com.h2database:h2:1.4.200'<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0443\u043b\u0438 Spring Framework \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043b\u0438\u0448\u043d\u0438\u043c\u0438, \u0442\u0435\u043c \u0431\u043e\u043b\u0435\u0435, \u0447\u0442\u043e \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 Spring. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u044f \u0438\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u043b\u0435\u0433\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u0435\u0439.<\/p>\n<p>\u0421\u0430\u043c\u044b\u0435 \u0432\u0430\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 &#8212; \u044d\u0442\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 Keycloak. \u0412 \u0432\u043e\u043f\u0440\u043e\u0441\u0435 \u043d\u043e\u043c\u0435\u0440\u043e\u0432 \u0432\u0435\u0440\u0441\u0438\u0439 \u044d\u0442\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u0430\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u0432\u0435\u0440\u0441\u0438\u044f Keycloak \u0443 \u0432\u0430\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f.<\/p>\n<p>\u0414\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d JBoss Logging, \u043a\u0430\u043a &#171;\u0440\u043e\u0434\u043d\u043e\u0435&#187; \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 WildFly. \u0414\u043e\u043f\u0443\u0441\u043a\u0430\u044e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u043b\u043e\u0433, \u043f\u043e\u043b\u044c\u0437\u0443\u044f\u0441\u044c \u0447\u0435\u043c-\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c, \u0432\u0440\u043e\u0434\u0435 SLF4J.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c <code>io.freefair.lombok<\/code>.<\/p>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 Keycloak \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c <code>org.keycloak.storage.UserStorageProvider<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0422\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0432 \u0442\u0430\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u0445, \u043a\u0430\u043a <code>org.keycloak.storage.user.UserLookupProvider<\/code> \u0438 <code>org.keycloak.credential.CredentialInputValidator<\/code>. \u041f\u0435\u0440\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0438\u043c\u0435\u043d\u0438, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441\u0443 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b. \u0412\u0442\u043e\u0440\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 &#8212; \u043c\u0435\u0442\u043e\u0434 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f. \u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.UserModel<\/code> (\u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0451 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439). \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 <code>org.keycloak.storage.adapter.AbstractUserAdapter<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.UserModel<\/code>:<\/p>\n<pre><code class=\"java\">public class LegacyDatabaseUserModel extends AbstractUserAdapter {     public static final String ATTRIBUTE_PASSWORD = \"password\";        private final MultivaluedHashMap&lt;String, String&gt; attributes = new MultivaluedHashMap&lt;&gt;();     private final Set&lt;RoleModel&gt; roles;      private LegacyDatabaseUserModel(Builder builder) {         super(builder.session, builder.realm, builder.storageProviderModel);         this.attributes.putSingle(UserModel.USERNAME, builder.username);         this.attributes.putSingle(UserModel.FIRST_NAME, builder.firstName);         this.attributes.putSingle(UserModel.LAST_NAME, builder.lastName);         this.attributes.putSingle(ATTRIBUTE_PASSWORD, builder.password);         this.roles = Collections.unmodifiableSet(builder.roles);     }      public static Builder builder() {         return new Builder();     }        @Override     public String getUsername() {         return getFirstAttribute(UserModel.USERNAME);     }      @Override     public String getFirstName() {         return getFirstAttribute(UserModel.FIRST_NAME);     }      @Override     public String getLastName() {         return getFirstAttribute(UserModel.LAST_NAME);     }        @Override     public Map&lt;String, List&lt;String&gt;&gt; getAttributes() {         return new MultivaluedHashMap&lt;&gt;(attributes);     }      @Override     public String getFirstAttribute(String name) {         return attributes.getFirst(name);     }      @Override     public List&lt;String&gt; getAttribute(String name) {         return attributes.get(name);     }      @Override     protected Set&lt;RoleModel&gt; getRoleMappingsInternal() {         return roles;     }      public static class Builder {         ...     } }<\/code><\/pre>\n<p>\u041d\u0430\u0431\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0432\u0435\u043b\u0438\u043a, \u0430 \u0441\u0430\u043c\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b, \u0447\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u043a\u043b\u0430\u0441\u0441\u043e\u043c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u044b-\u043c\u0443\u0442\u0430\u0442\u043e\u0440\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0440\u043e\u0441\u0430\u044e\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. \u041f\u043e\u0447\u0435\u043c\u0443 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u0432 <code>Map<\/code>, \u0430 \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430, \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 &#8212; \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 <code>org.keycloak.models.RoleModel<\/code> \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0414\u043b\u044f \u043d\u0435\u0451 \u044f \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0441\u0435\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"java\">@AllArgsConstructor public class LegacyDatabaseRoleModel implements RoleModel {     @Getter     private final RoleContainerModel container;     @Getter     private final String name;      @Override     public String getId() {         return getName();     }      @Override     public void setName(String name) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public String getDescription() {         return null;     }      @Override     public void setDescription(String description) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public boolean isComposite() {         return false;     }      @Override     public void addCompositeRole(RoleModel role) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void removeCompositeRole(RoleModel role) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public Stream&lt;RoleModel&gt; getCompositesStream() {         return Stream.empty();     }      @Override     public boolean isClientRole() {         return false;     }      @Override     public String getContainerId() {         return container.getId();     }      @Override     public boolean hasRole(RoleModel role) {         return false;     }      @Override     public Map&lt;String, List&lt;String&gt;&gt; getAttributes() {         return Collections.emptyMap();     }      @Override     public void setSingleAttribute(String name, String value) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void setAttribute(String name, List&lt;String&gt; values) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public void removeAttribute(String name) {         throw new ReadOnlyException(\"Role is read only for this update\");     }      @Override     public Stream&lt;String&gt; getAttributeStream(String name) {         return Stream.empty();     } } <\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u0440\u043e\u043b\u0438 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u043b\u0438\u0448\u044c \u0438\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0440\u043e\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u0430\u043a\u0436\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0435\u0451 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u041f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u043e\u043c \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043c\u0435\u0442\u043e\u0434\u044b-\u043c\u0443\u0442\u0430\u0442\u043e\u0440\u044b \u0431\u0440\u043e\u0441\u0430\u044e\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0431\u0443\u0434\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u043e \u0435\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443. \u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c. \u041c\u0435\u0442\u043e\u0434 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"java\">private final ConcurrentMap&lt;UserModelKey, LegacyDatabaseUserModel&gt; loadedUsers = new ConcurrentHashMap&lt;&gt;();  @Override public LegacyDatabaseUserModel getUserByUsername(String username, RealmModel realm) {     UserModelKey userKey = new UserModelKey(username, realm.getId());     return loadedUsers.computeIfAbsent(userKey, k -&gt; {         LegacyDatabaseUserModel user = findUserByName(username, realm);         if (user != null) {             log.debugv(\"User is loaded by name \\\"{0}\\\"\", username);         }         return user;     }); }<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0435\u0437\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Keycloak \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430. \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0451\u043d \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441 <code>java.util.concurrent.ConcurrentMap<\/code>. \u041c\u0435\u0442\u043e\u0434 <code>findUserByName<\/code> \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>org.springframework.jdbc.core.JdbcTemplate<\/code>&nbsp;\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <code>org.springframework.jdbc.core.ResultSetExtractor<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u043f\u043e\u043b\u043e\u0447\u043a\u0430\u043c, \u043f\u043e\u043f\u0443\u0442\u043d\u043e \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044f \u043d\u0430\u0431\u043e\u0440 \u0435\u0433\u043e \u0440\u043e\u043b\u0435\u0439.<\/p>\n<pre><code class=\"java\">private LegacyDatabaseUserModel findUserByName(String username, RealmModel realm) { \treturn jdbcTemplate.query(SQL_FIND_USER_BY_NAME, new Object[]{username}, new int[]{Types.VARCHAR}, \t\t\t\tnew LegacyDatabaseUserModelResultSetExtractor(realm)); }<\/code><\/pre>\n<pre><code class=\"java\">@RequiredArgsConstructor private class LegacyDatabaseUserModelResultSetExtractor implements ResultSetExtractor&lt;LegacyDatabaseUserModel&gt; {     final RealmModel realm;      @Override     public LegacyDatabaseUserModel extractData(ResultSet rs) throws SQLException, DataAccessException {         if (!rs.next()) {             return null;         }          LegacyDatabaseUserModel.Builder userModelBuilder = LegacyDatabaseUserModel.builder()                 .session(session)                 .realm(realm)                 .storageProviderModel(storageProviderModel)                 .username(rs.getString(1))                 .password(rs.getString(2))  <\/code><\/pre>\n<\/hr>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-320859","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/320859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=320859"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/320859\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=320859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=320859"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=320859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}