{"id":463658,"date":"2025-06-17T15:00:48","date_gmt":"2025-06-17T15:00:48","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=463658"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=463658","title":{"rendered":"<span>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043b\u044f Keycloak: \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e API<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><em>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0434\u043b\u044f \u043b\u044e\u0434\u0435\u0439, \u0438\u043c\u0435\u044e\u0449\u0438\u0445 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 java \u0438 \u0442\u0435\u0445, \u043a\u0442\u043e \u0445\u043e\u0442\u044f \u0431\u044b \u0447\u0442\u043e-\u0442\u043e \u0441\u043b\u044b\u0448\u0430\u043b \u043e <\/em><a href=\"https:\/\/www.keycloak.org\/\" rel=\"noopener noreferrer nofollow\"><em>Keycloak<\/em><\/a><em>. \u0412 \u043d\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043d\u043e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u043b\u0430\u0433\u0438\u043d, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0440\u0430\u0431\u043e\u0430\u0442\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 API \u0441\u0435\u0440\u0432\u0438\u0441. \u042f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0442\u0435\u043c\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u044f\u0437\u044b\u043a\u043e\u043c \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435.<\/em><\/p>\n<p>\u041a\u043e\u043b\u043b\u0435\u0433\u0438, \u043d\u0430 \u0441\u0432\u044f\u0437\u0438 <a href=\"https:\/\/t.me\/notReadySir\" rel=\"noopener noreferrer nofollow\">\u0412\u0430\u0448 \u043a\u043e\u0434 \u0435\u0449\u0435 \u043d\u0435 \u0433\u043e\u0442\u043e\u0432, \u0441\u044d\u0440!<\/a> \u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e Keycloak, \u0430 \u0442\u043e\u0447\u043d\u0435\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443.<\/p>\n<p>\u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0432\u043e\u043f\u0440\u043e\u0441, \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 Keycloak? \u041f\u043e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438, \u0432\u043e\u0437\u044c\u043c\u0443 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0438\u0437 \u0412\u0438\u043a\u0438.<\/p>\n<blockquote>\n<p>Keycloak \u2014 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a OAuth 2.0 \u0438 OpenID Connect, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0435\u0434\u0438\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 (SSO) \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/blockquote>\n<p>\u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0447\u0435\u0442\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0432\u0441\u0435, \u0447\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u0430\u043d\u043d\u0443\u044e \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e, \u043d\u043e \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u0430\u0432\u0442\u043e\u0440\u044b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442. \u041a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0432 <strong>\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<blockquote>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c SPI \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (User Storage SPI), \u0447\u0442\u043e\u0431\u044b \u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f Keycloak, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0431\u0430\u0437\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430\u043c \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 LDAP \u0438 Active Directory \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u043e\u0433\u043e SPI \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Keycloak \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0432\u043e\u044e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0434\u043d\u0430\u043a\u043e \u0447\u0430\u0441\u0442\u043e \u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u043f\u0440\u0438\u0435\u0442\u0430\u0440\u043d\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u043c\u0438\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 Keycloak. \u0412 \u0442\u0430\u043a\u0438\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 User Storage SPI, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0435\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e Keycloak \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u0445 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c Keycloak \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0441\u0432\u043e\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0432\u0441\u0435, \u0447\u0442\u043e  \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432. \u0421\u043f\u0438\u0441\u043e\u043a \u044d\u0442\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0438 \u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u044b \u043d\u0438\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u043e\u0439\u0434\u0435\u043c \u0434\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<p>\u0410 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u042f \u043f\u043e\u043b\u0430\u0433\u0430\u044e, \u0447\u0442\u043e \u043d\u0430 \u0432\u0430\u0448\u0435\u0439 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b java, maven \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"bash\">mvn archetype:generate -DgroupId=org.example -DartifactId=keycloak-api-plugin -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false <\/code><\/pre>\n<p>\u0420\u0438\u0441. 1 \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<p>\u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u043c <strong>pom.xml<\/strong> \u0444\u0430\u0439\u043b:<\/p>\n<pre><code class=\"xml\">&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"   xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 http:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;   &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;    &lt;groupId&gt;org.example&lt;\/groupId&gt;   &lt;artifactId&gt;keycloak_api_plugin&lt;\/artifactId&gt;   &lt;version&gt;1.0-SNAPSHOT&lt;\/version&gt;   &lt;packaging&gt;jar&lt;\/packaging&gt;    &lt;name&gt;keycloak_api_plugin&lt;\/name&gt;   &lt;url&gt;http:\/\/maven.apache.org&lt;\/url&gt;    &lt;properties&gt;     &lt;keycloak.version&gt;26.2.2&lt;\/keycloak.version&gt;     &lt;postgresql.version&gt;42.7.3&lt;\/postgresql.version&gt;     &lt;maven.compiler.source&gt;17&lt;\/maven.compiler.source&gt;     &lt;maven.compiler.target&gt;17&lt;\/maven.compiler.target&gt;     &lt;project.build.sourceEncoding&gt;UTF-8&lt;\/project.build.sourceEncoding&gt;   &lt;\/properties&gt;    &lt;repositories&gt;     &lt;repository&gt;       &lt;id&gt;keycloak-releases&lt;\/id&gt;       &lt;url&gt;https:\/\/s01.oss.sonatype.org\/content\/repositories\/releases&lt;\/url&gt;     &lt;\/repository&gt;   &lt;\/repositories&gt;    &lt;dependencies&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-core&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;       &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;       &lt;version&gt;2.13.4.2&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-server-spi&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-server-spi-private&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-services&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;javax.ws.rs&lt;\/groupId&gt;       &lt;artifactId&gt;javax.ws.rs-api&lt;\/artifactId&gt;       &lt;version&gt;2.1&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;       &lt;artifactId&gt;jackson-annotations&lt;\/artifactId&gt;       &lt;version&gt;2.13.3&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.jboss.resteasy&lt;\/groupId&gt;       &lt;artifactId&gt;resteasy-client&lt;\/artifactId&gt;       &lt;version&gt;3.15.1.Final&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;       &lt;artifactId&gt;lombok&lt;\/artifactId&gt;       &lt;version&gt;1.18.38&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.jboss.resteasy&lt;\/groupId&gt;       &lt;artifactId&gt;resteasy-jackson2-provider&lt;\/artifactId&gt;       &lt;version&gt;3.15.1.Final&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.mindrot&lt;\/groupId&gt;       &lt;artifactId&gt;jbcrypt&lt;\/artifactId&gt;       &lt;version&gt;0.4&lt;\/version&gt;     &lt;\/dependency&gt;   &lt;\/dependencies&gt;   &lt;build&gt;     &lt;plugins&gt;       &lt;plugin&gt;         &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;         &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt;         &lt;version&gt;3.11.0&lt;\/version&gt;         &lt;configuration&gt;           &lt;source&gt;${maven.compiler.source}&lt;\/source&gt;           &lt;target&gt;${maven.compiler.target}&lt;\/target&gt;         &lt;\/configuration&gt;       &lt;\/plugin&gt;       &lt;plugin&gt;         &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;         &lt;artifactId&gt;maven-shade-plugin&lt;\/artifactId&gt;         &lt;version&gt;3.5.1&lt;\/version&gt;         &lt;executions&gt;           &lt;execution&gt;             &lt;phase&gt;package&lt;\/phase&gt;             &lt;goals&gt;               &lt;goal&gt;shade&lt;\/goal&gt;             &lt;\/goals&gt;             &lt;configuration&gt;               &lt;artifactSet&gt;                 &lt;includes&gt;                   &lt;include&gt;org.mindrot:jbcrypt&lt;\/include&gt;                 &lt;\/includes&gt;               &lt;\/artifactSet&gt;             &lt;\/configuration&gt;           &lt;\/execution&gt;         &lt;\/executions&gt;       &lt;\/plugin&gt;     &lt;\/plugins&gt;   &lt;\/build&gt; &lt;\/project&gt; <\/code><\/pre>\n<p>\u0420\u0438\u0441. 2 \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c POM-\u0444\u0430\u0439\u043b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Keycloak, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a keycloak-core, keycloak-server-spi, keycloak-server-spi-private \u0438 keycloak-services, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JSON, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a jackson-databind \u0438 jackson-annotations, \u0430 \u0442\u0430\u043a\u0436\u0435 JAX-RS \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0432\u043a\u043b\u044e\u0447\u0430\u044f resteasy-client \u0438 resteasy-jackson2-provider. \u0412\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a Keycloak, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0440\u0441\u0438\u044e, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.<\/p>\n<pre><code>keycloak-api-plugin \u2502 \u251c\u2500\u2500 pom.xml \u2502 \u2514\u2500\u2500 src     \u2514\u2500\u2500 main         \u2514\u2500\u2500 java             \u2514\u2500\u2500 org                 \u2514\u2500\u2500 example                     \u2514\u2500\u2500 keycloakapiplugin                         \u251c\u2500\u2500 adapter                         \u2502   \u2514\u2500\u2500 UserAdapter.java                         \u2502                         \u251c\u2500\u2500 api                         \u2502   \u2514\u2500\u2500 UsersApiService.java                         \u2502                         \u251c\u2500\u2500 dto                         \u2502   \u2514\u2500\u2500 UserResponseDto.java                         \u2502                         \u251c\u2500\u2500 entity                         \u2502   \u2514\u2500\u2500 UserEntity.java                         \u2502                         \u251c\u2500\u2500 mapper                         \u2502   \u2514\u2500\u2500 UserMapper.java                         \u2502                         \u251c\u2500\u2500 ExternalUserStorageProvider.java                         \u2502                         \u2514\u2500\u2500 ExternalUserStorageProviderFactory.java  <\/code><\/pre>\n<p>\u0420\u0438\u0441. 3 \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438 \u0432\u044b\u044f\u0441\u043d\u0438\u043c, \u0437\u0430 \u0447\u0442\u043e \u043e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442.<\/p>\n<ol>\n<li>\n<p><strong>ExternalUserStorageProviderFactory<\/strong> &#8212; \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u0441\u043e\u0437\u0434\u0430\u044e\u0449\u0430\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b ExternalUserStorageProvider. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0438\u043c\u044f \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043b\u044f Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>ExternalUserStorageProvider<\/strong> &#8212; \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 (\u043f\u043e\u0438\u0441\u043a, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0437\u0430\u043f\u0440\u043e\u0441\u044b). \u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>UserEntity<\/strong> &#8212; \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0430\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f (id, username, email \u0438 \u0442.\u0434.). \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>UserResponseDto<\/strong> &#8212; DTO \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e API. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JSON.<\/p>\n<\/li>\n<li>\n<p><strong>UsersApiService<\/strong> &#8212; \u0421\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\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 \u0440\u0430\u0437\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>UserAdapter<\/strong> &#8212; \u0410\u0434\u0430\u043f\u0442\u0435\u0440, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0438\u0439 UserEntity \u0432 \u043c\u043e\u0434\u0435\u043b\u044c Keycloak. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f Keycloak.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432 \u043e\u0431\u0449\u0438\u0445 \u0447\u0435\u0440\u0442\u0430\u0445 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u043a\u0430\u043a\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0437\u0430 \u0447\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0430\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0430 <strong>User Storage SPI.<\/strong><\/p>\n<blockquote>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u0440\u0435\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Keycloak \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u043e\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0440\u044f\u0434 \u0448\u0430\u0433\u043e\u0432 \u0434\u043b\u044f \u0435\u0433\u043e \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0435\u0441\u0442\u044c \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u043a\u044d\u0448\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439; \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0439\u0434\u0435\u043d, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u044d\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438. \u0417\u0430\u0442\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0438\u0449\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 Keycloak. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d, \u043e\u043d\u0430 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432<br \/> User Storage SPI, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u043e\u043a\u0430 \u043e\u0434\u0438\u043d \u0438\u0437 \u043d\u0438\u0445 \u043d\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u0438\u0441\u043a\u043e\u043c\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0432\u043d\u0435\u0448\u043d\u0435\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043d\u0435\u0448\u043d\u0435\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043c\u0435\u0442\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Keycloak.<br \/> \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 User Storage SPI \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 CRUD \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043d\u043e\u0433\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u042d\u0442\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430.<\/p>\n<\/blockquote>\n<p>\u0418\u0437 \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435, \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u044b\u0432\u043e\u0434, \u0447\u0442\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u00ab\u0444\u0438\u0433\u0443\u0440\u043e\u0439\u00bb \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u0441 \u043d\u0435\u0433\u043e \u043c\u044b \u0438 \u043d\u0430\u0447\u043d\u0435\u043c.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c UserEntity \u0441 \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 keycloak:<\/p>\n<pre><code class=\"java\"> package org.example.keycloakapiplugin.entity;  import lombok.Getter; import lombok.Setter;  import java.util.Date;  @Getter @Setter public class UserEntity {     private String id;     private String username;     private String email;     private String password;     private Date createdAt;     private String phone; } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 4 \u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<p>\u042d\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 Keycloak \u0438 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<p>\u0412 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043e\u0442\u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"java\">package org.example.keycloakapiplugin.dto;  import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.*;  import java.util.Date;  @Getter @Setter @AllArgsConstructor @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public class UserResponseDto {     private String id;     private String username;     private String email;     private String password;     private String phone;     private Date createdAt; } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 5 DTO \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0440\u044f\u0434 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 endpoints:<\/p>\n<ol>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439: <strong>GET \/users<\/strong> &#8212; \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f: <strong>POST \/users<\/strong> &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e ID: <strong>GET \/users\/{id}<\/strong> &#8212; \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u043c\u0443 ID.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0438\u043c\u0435\u043d\u0438: <strong>GET \/users\/username\/{username}<\/strong> &#8212; \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0438\u043c\u0435\u043d\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e email: <strong>GET \/users\/email\/{email}<\/strong> &#8212; \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e ID: <strong>DELETE \/users\/{id}<\/strong> &#8212; \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u043c\u0443 ID.<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0439 \u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u043d\u0430\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 (UserEntity) \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043c\u0430\u043f\u043f\u0435\u0440, \u0437\u0430\u0434\u0430\u0447\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0442\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439.<\/p>\n<pre><code class=\"java\">package org.example.keycloakapiplugin.mapper;   import org.example.keycloakapiplugin.entity.UserEntity; import org.example.keycloakapiplugin.dto.UserResponseDto;  import java.util.List; import java.util.stream.Collectors;  public class UserMapper {      public static UserEntity mapToUserEntity(UserResponseDto dto) {         if (dto == null) {             return null;         }          UserEntity userEntity = new UserEntity();         userEntity.setEmail(dto.getEmail());         userEntity.setPhone(dto.getPhone());         userEntity.setUsername(dto.getEmail());         userEntity.setId(dto.getId());         userEntity.setPassword(dto.getPassword());         userEntity.setCreatedAt(dto.getCreatedAt());          return userEntity;     }      public static List&lt;UserEntity&gt; mapToUserEntityList(List&lt;UserResponseDto&gt; dtoList) {         return dtoList.stream().map(UserMapper::mapToUserEntity).collect(Collectors.toUnmodifiableList());     } }  <\/code><\/pre>\n<p>\u0420\u0438\u0441. 6 \u041a\u043b\u0430\u0441\u0441 UserMapper \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 DTO \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u043f\u0438\u0441\u043a\u0438<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c \u043a\u043b\u0430\u0441\u0441, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0440\u044f\u0434 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0445 \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0430\u043f\u0438. \u0415\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 &#8212; \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e http \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0442\u0432\u0435\u0442\u043e\u0432.<\/p>\n<pre><code class=\"java\">package org.example.keycloakapiplugin.mapper.api;  import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.Getter; import lombok.Setter; import org.jboss.logging.Logger; import org.keycloak.storage.StorageId; import org.example.keycloakapiplugin.mapper.dto.UserResponseDto; import org.example.keycloakapiplugin.mapper.entity.UserEntity; import org.example.keycloakapiplugin.mapper.mapper.UserMapper;  import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Collections; import java.util.List;  public class UsersApiService {      private static final ObjectMapper objectMapper = new ObjectMapper();     private static final Logger logger = Logger.getLogger(UsersApiService.class);     private static final String USER_BY_USERNAME_URL = \"http:\/\/localhost:8081\/users\/username\/\";     private static final String USER_SERVICE_URL = \"http:\/\/localhost:8081\/users\";     private static final String USER_BY_ID_URL = \"http:\/\/localhost:8081\/users\/\";     private static final String USER_BY_EMAIL_URL = \"http:\/\/localhost:8081\/users\/email\/\";      private final HttpClient httpClient;      public UsersApiService() {         this.httpClient = HttpClient.newHttpClient();     }      @Data     @JsonIgnoreProperties(ignoreUnknown = true)     @Getter     @Setter     public static class ApiResponse {         @JsonIgnoreProperties(ignoreUnknown = true)         private List&lt;UserResponseDto&gt; data;     }      public List&lt;UserEntity&gt; getAllUsers() {          HttpRequest request = HttpRequest.newBuilder()                 .uri(URI.create(USER_SERVICE_URL))                 .header(\"Accept\", \"application\/json\")                 .GET()                 .build();          try {             HttpResponse&lt;String&gt; response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());              if (response.statusCode() == 200) {                 ApiResponse apiResponse = objectMapper.readValue(response.body(), ApiResponse.class);                  if (apiResponse != null &amp;&amp; apiResponse.getData() != null) {                     logger.infof(\"Successfully loaded %d users from external service\", apiResponse.getData().size());                     return UserMapper.mapToUserEntityList(apiResponse.getData());                 } else {                     logger.error(\"Received empty response from user service\");                 }             } else {                 logger.errorf(\"Failed to load users from service. Status code: %d, Response: %s\",                         response.statusCode(), response.body());             }         } catch (IOException | InterruptedException e) {             logger.error(\"Error while fetching users from external service\", e);             if (e instanceof InterruptedException) {                 Thread.currentThread().interrupt();             }         }          return Collections.emptyList();     }      public UserEntity getUserByUsername(String username) {         try {             HttpRequest request = HttpRequest.newBuilder()                     .uri(URI.create(USER_BY_USERNAME_URL + username))                     .header(\"Accept\", \"application\/json\")                     .GET()                     .build();              HttpResponse&lt;String&gt; response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());              if (response.statusCode() == 200) {                 UserResponseDto userResponse = objectMapper.readValue(response.body(), UserResponseDto.class);                 return UserMapper.mapToUserEntity(userResponse);             }         } catch (IOException | InterruptedException e) {             logger.error(\"Error while fetching user by username from external service\", e);             if (e instanceof InterruptedException) {                 Thread.currentThread().interrupt();             }         }          logger.info(\"could not find getUserByUsername: \" + username);         return null;     }      public UserEntity getUserById(String id) {         logger.info(\"getUserById: \" + id);         String externalId = StorageId.externalId(id);          try {             HttpRequest request = HttpRequest.newBuilder()                     .uri(URI.create(USER_BY_ID_URL + externalId))                     .header(\"Accept\", \"application\/json\")                     .GET()                     .build();              HttpResponse&lt;String&gt; response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());              if (response.statusCode() == 200) {                 UserResponseDto userResponse = objectMapper.readValue(response.body(), UserResponseDto.class);                 return UserMapper.mapToUserEntity(userResponse);             } else {                 logger.error(\"Failed to get user by id. Status code: %d, Response: %s\");             }         } catch (IOException | InterruptedException e) {             logger.error(\"Error while fetching user by id from external service\", e);             if (e instanceof InterruptedException) {                 Thread.currentThread().interrupt();             }         }          logger.info(\"could not find user by id: \" + id);         return null;     }      public UserEntity getUserByEmail(String email) {         logger.info(\"getUserByEmail: \" + email);          try {             HttpRequest request = HttpRequest.newBuilder()                     .uri(URI.create(USER_BY_EMAIL_URL + email))                     .header(\"Accept\", \"application\/json\")                     .GET()                     .build();              HttpResponse&lt;String&gt; response = this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());              if (response.statusCode() == 200) {                 UserResponseDto userResponse = objectMapper.readValue(response.body(), UserResponseDto.class);                 return UserMapper.mapToUserEntity(userResponse);             } else {                 logger.error(\"Failed to get user by email. Status code: %d, Response: %s\");             }         } catch (IOException | InterruptedException e) {             logger.error(\"Error while fetching user by email from external service\", e);             if (e instanceof InterruptedException) {                 Thread.currentThread().interrupt();             }         }         return null;     } } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 7 \u041a\u043b\u0430\u0441\u0441 <code>UsersApiService<\/code> \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0427\u0442\u043e \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435? \u0412 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 httpClient \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043d\u0430\u0431\u043e\u0440 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0434\u0438\u043d \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e <strong>getAllUsers<\/strong>.<\/p>\n<pre><code class=\"java\">\/**  * \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0447\u0435\u0440\u0435\u0437 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441.  *   * @return \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 UserEntity \u0438\u043b\u0438 \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438  *\/ public List&lt;UserEntity&gt; getAllUsers() {     \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c HTTP GET-\u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 URL     HttpRequest request = HttpRequest.newBuilder()             .uri(URI.create(USER_SERVICE_URL))  \/\/ URL \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439             .header(\"Accept\", \"application\/json\")  \/\/ \u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 JSON-\u0444\u043e\u0440\u043c\u0430\u0442\u0435             .GET()             .build();      try {         \/\/ \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442         HttpResponse&lt;String&gt; response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());          \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 (HTTP 200)         if (response.statusCode() == 200) {             \/\/ \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c JSON-\u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 ApiResponse             ApiResponse apiResponse = objectMapper.readValue(response.body(), ApiResponse.class);              \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435             if (apiResponse != null &amp;&amp; apiResponse.getData() != null) {                 logger.infof(\"Successfully loaded %d users from external service\", apiResponse.getData().size());                 \/\/ \u041c\u0430\u043f\u043f\u0438\u043c DTO \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043d\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 Keycloak                 return UserMapper.mapToUserEntityList(apiResponse.getData());             } else {                 logger.error(\"Received empty response from user service\");             }         } else {             \/\/ \u041b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u043c \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u043e\u0442\u0432\u0435\u0442\u0430             logger.errorf(\"Failed to load users from service. Status code: %d, Response: %s\",                     response.statusCode(), response.body());         }     } catch (IOException e) {         \/\/ \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430         logger.error(\"Error while fetching users from external service\", e);     } catch (InterruptedException e) {         \/\/ \u041e\u0441\u043e\u0431\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0442\u043e\u043a\u0430         logger.error(\"Request was interrupted\", e);         Thread.currentThread().interrupt();  \/\/ \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0444\u043b\u0430\u0433 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u044f     }      \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043b\u044e\u0431\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a     return Collections.emptyList(); } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 8 \u041c\u0435\u0442\u043e\u0434 getAllUsers<\/p>\n<p>\u041c\u0435\u0442\u043e\u0434 getAllUsers \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u043b\u0434\u0435\u0440\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0430\u0434\u0440\u0435\u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0442\u0438\u043f \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u043e\u0442\u0432\u0435\u0442\u0430, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0430\u043f\u044f\u0442\u0441\u044f \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c UserEntity, \u0438 \u043c\u0435\u0442\u043e\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043d\u0430 \u043d\u0438\u0445 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0441\u0430\u043c\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Provider \u0432 Keycloak.<br \/> \u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u043e\u0433\u0440\u0443\u0437\u0438\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0439 \u0440\u0430\u0437 \u043e\u0431\u0440\u0430\u0442\u0438\u043c\u0441\u044f \u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u043a \u0440\u0430\u0437\u0434\u0435\u043b\u0443 <strong>Capability Interfaces<\/strong> \u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0438 \u0437\u0430\u0447\u0435\u043c \u043e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b.<\/p>\n<blockquote>\n<p>\u0415\u0441\u043b\u0438 \u043a\u0440\u0430\u0442\u043a\u043e, \u0442\u043e \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0441\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 UserStorageProvider (\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432 Keycloak, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 Keycloak) \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0438\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438. \u042d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u0445, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043b\u0438\u0448\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0418 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0432\u0430\u0448\u0438\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044f\u043c \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c.<\/p>\n<\/blockquote>\n<p>\u0418 \u0442\u0430\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043d\u0430\u043c \u043b\u044e\u0431\u0435\u0437\u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0445:<\/p>\n<blockquote>\n<p><strong>org.keycloak.storage.user.UserLookupProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c, \u0442\u0430\u043a\u0438\u043c \u043a\u0430\u043a \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440, \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0447\u0442\u0430.<br \/> <strong>org.keycloak.storage.user.UserQueryMethodsProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043a\u043e\u043d\u0441\u043e\u043b\u044c, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.<br \/> <strong>org.keycloak.storage.user.UserCountMethodsProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u0434\u0441\u0447\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c \u043f\u043e\u0438\u0441\u043a\u0430.<br \/> <strong>org.keycloak.storage.user.UserQueryProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c UserQueryMethodsProvider \u0438 UserCountMethodsProvider. \u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0442\u0430\u043a \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0438\u0445 \u043f\u043e\u0434\u0441\u0447\u0435\u0442.<br \/> <strong>org.keycloak.storage.user.UserRegistrationProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0432\u043d\u0435\u0448\u043d\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435. \u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445.<br \/> <strong>org.keycloak.storage.user.UserBulkUpdateProvider<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u0437\u0430\u043f\u0438\u0441\u044f\u043c\u0438.<br \/> <strong>org.keycloak.credential.CredentialInputValidator<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u043f\u0430\u0440\u043e\u043b\u0438. \u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<br \/> <strong>org.keycloak.credential.CredentialInputUpdater<\/strong><br \/> \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u043f\u0430\u0440\u043e\u043b\u0438 \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<\/blockquote>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0430\u0434\u043c\u0438\u043d\u043a\u0438, \u0442\u043e \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0431\u043e\u0440 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432:<\/p>\n<blockquote>\n<p>UserStorageProvider,<br \/> UserLookupProvider,<br \/> UserQueryProvider,<br \/> CredentialInputValidator,<br \/> CredentialInputUpdater<\/p>\n<\/blockquote>\n<p>\u042d\u0442\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u043d\u0430\u0431\u043e\u0440 \u0441\u0430\u043c\u043e\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0442\u0440\u0430\u0436\u0430\u044e\u0442 \u0441\u0443\u0442\u044c \u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, getUserById \u0438\u043b\u0438 searchForUserStream. \u041c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0443\u0433\u043b\u0443\u0431\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0434\u0435\u0442\u0430\u043b\u0438 \u0438\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043b\u0438\u0448\u044c \u043e\u0442\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u044d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0437 \u043a\u043b\u0430\u0441\u0441\u0430 UsersApiService \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0430 \u0437\u0430\u0442\u0435\u043c, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432 UserModel.<\/p>\n<pre><code class=\"java\">package org.example.keycloakapiplugin;  import org.keycloak.component.ComponentModel; import org.keycloak.credential.CredentialInput; import org.keycloak.credential.CredentialInputUpdater; import org.keycloak.credential.CredentialInputValidator; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.credential.PasswordCredentialModel; import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.user.UserLookupProvider; import org.keycloak.storage.user.UserQueryProvider; import org.mindrot.jbcrypt.BCrypt; import org.example.keycloakapiplugin.adapter.UserAdapter; import org.example.keycloakapiplugin.api.UsersApiService; import org.example.keycloakapiplugin.entity.UserEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory;  import java.util.Map; import java.util.stream.Stream;  public class ExternalUserStorageProvider implements         UserStorageProvider,         UserLookupProvider,         UserQueryProvider,         CredentialInputValidator,         CredentialInputUpdater {      protected KeycloakSession session;     protected ComponentModel model;     private static final Logger logger = LoggerFactory.getLogger(ExternalUserStorageProvider.class);       private final UsersApiService usersApiService;      public ExternalUserStorageProvider(KeycloakSession session, ComponentModel model) {         logger.debug(\"Creating new PropertyFileUserStorageProvider instance\");         this.session = session;         this.model = model;         this.usersApiService = new UsersApiService();     }      @Override     public UserModel getUserByUsername(RealmModel realm, String username) {         logger.info(\"getUserByUsername: \" + username);          UserEntity userEntity = this.usersApiService.getUserByUsername(username);         if (userEntity != null) {             return new UserAdapter(session, realm, model, userEntity);         } else {             logger.error(\"Failed to get user by username. Status code: %d, Response: %s\");         }          logger.info(\"could not find getUserByUsername: \" + username);         return null;     }      @Override     public UserModel getUserById(RealmModel realm, String id) {         UserEntity userEntity = this.usersApiService.getUserById(id);                 if (userEntity != null) {                     return new UserAdapter(session, realm, model, userEntity);             } else {                 logger.error(\"Failed to get user by id. Status code: %d, Response: %s\");             }         logger.info(\"could not find user by id: \" + id);         return null;     }      @Override     public UserModel getUserByEmail(RealmModel realm, String email) {         logger.info(\"getUserByEmail: \" + email);              UserEntity userEntity = this.usersApiService.getUserByEmail(email);                  if (userEntity != null) {                     return new UserAdapter(session, realm, model, userEntity);             } else {                 logger.error(\"Failed to get user by email. Status code: %d, Response: %s\");             }             logger.error(\"Error while fetching user by email from external service\");          return null;     }      @Override     public boolean supportsCredentialType(String credentialType) {         logger.debug(\"Checking support for credential type: {}\", credentialType);         boolean supported = PasswordCredentialModel.TYPE.equals(credentialType);         logger.debug(\"Credential type {} supported: {}\", credentialType, supported);         return supported;     }      @Override     public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {         logger.debug(\"Checking if credential type {} is configured for user: {}\", credentialType, user.getUsername());          boolean configured = supportsCredentialType(credentialType) &amp;&amp;                 this.usersApiService.getUserByUsername(user.getUsername()) != null;         logger.debug(\"Credential type {} configured for user {}: {}\", credentialType, user.getUsername(), configured);         return configured;     }      @Override     public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {         logger.debug(\"Validating credential for user: {}\", user.getUsername());          if (!supportsCredentialType(input.getType())) {             logger.debug(\"Credential type not supported: {}\", input.getType());             return false;         }          UserEntity userEntity = this.usersApiService.getUserByUsername(user.getUsername());         if (userEntity == null) {             logger.debug(\"User entity not found for username: {}\", user.getUsername());             return false;         }          \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0445\u044d\u0448 \u043f\u0430\u0440\u043e\u043b\u044f \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b         String storedPasswordHash = userEntity.getPassword();          String inputPassword = input.getChallengeResponse();          return BCrypt.checkpw(inputPassword, storedPasswordHash);     }      @Override     public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {         logger.warn(\"Attempt to update credential for read-only storage provider, user: {}\", user.getUsername());         throw new ReadOnlyException(\"User storage is read-only\");     }      @Override     public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {         logger.debug(\"disableCredentialType called for user: {}, type: {}\", user.getUsername(), credentialType);         \/\/ Read-only - \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u043c     }      @Override     public Stream&lt;String&gt; getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {         logger.debug(\"getDisableableCredentialTypesStream called for user: {}\", user.getUsername());         return Stream.empty();     }      @Override     public void close() {         logger.info(\"Provider closed successfully\");     }      @Override     public Stream&lt;UserModel&gt; searchForUserStream(RealmModel realm, Map&lt;String, String&gt; params, Integer firstResult, Integer maxResults) {         String search = params.get(UserModel.SEARCH);         String lower = search != null ? search.toLowerCase() : \"\";          logger.debug(\"Searching for users with search term: {}\", lower);          Stream&lt;UserModel&gt; userStream = this.usersApiService.getAllUsers().stream()                 .filter(userEntity -&gt; userEntity.getUsername().toLowerCase().contains(lower) ||                         userEntity.getEmail().toLowerCase().contains(lower))                 .map(entity -&gt; new UserAdapter(session, realm, model, entity));          \/\/ \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434         if (firstResult != null) {             userStream = userStream.skip(firstResult);         }         if (maxResults != null) {             userStream = userStream.limit(maxResults);         }          return userStream;     }      @Override     public Stream&lt;UserModel&gt; getGroupMembersStream(RealmModel realmModel, GroupModel groupModel, Integer integer, Integer integer1) {         return Stream.empty();     }      @Override     public Stream&lt;UserModel&gt; searchForUserByUserAttributeStream(RealmModel realmModel, String s, String s1) {         return Stream.empty();     } } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 9 \u041a\u043b\u0430\u0441\u0441 <code>ExternalUserStorageProvider<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b Keycloak \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438, \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c<\/p>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u0441\u0442\u043e\u0438\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e UserModel. \u0427\u0442\u043e \u0436\u0435 \u044d\u0442\u043e \u0437\u0430 \u00ab\u043c\u043e\u0434\u0435\u043b\u044c\u00bb?<\/p>\n<p><strong>UserModel<\/strong> \u2014 \u043e\u0434\u0438\u043d \u0438\u0437 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 Keycloak. \u042d\u0442\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u0423 \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044f \u043c\u043e\u0436\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441: \u043a\u0430\u043a \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 UserEntity \u0432 UserModel, \u0435\u0441\u043b\u0438 UserApiService \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e UserEntity \u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e UserModel? \u0418 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0430\u0432.<\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c <strong>UserAdapter<\/strong>. \u042d\u0442\u043e\u0442 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c UserEntity (\u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b) \u0432 \u043c\u043e\u0434\u0435\u043b\u044c Keycloak (UserModel).<\/p>\n<pre><code class=\"java\">package org.example.keycloakapiplugin.adapter;  import org.jboss.logging.Logger; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.storage.StorageId; import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage; import org.example.keycloakapiplugin.entity.UserEntity;  import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Stream;  public class UserAdapter extends AbstractUserAdapterFederatedStorage {     private static final Logger logger = Logger.getLogger(UserAdapter.class);     protected UserEntity entity;     protected String keycloakId;      public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, UserEntity entity) {         super(session, realm, model);         this.entity = entity;         keycloakId = StorageId.keycloakId(model, entity.getId());     }      @Override     public String getUsername() {         return entity.getUsername();     }      @Override     public void setUsername(String username) {         entity.setUsername(username);      }      @Override     public void setEmail(String email) {         entity.setEmail(email);     }      @Override     public String getEmail() {         return entity.getEmail();     }      @Override     public String getId() {         return keycloakId;     }      @Override     public void setSingleAttribute(String name, String value) {         if (name.equals(\"phone\")) {             entity.setPhone(value);         } else {             super.setSingleAttribute(name, value);         }     }      @Override     public void removeAttribute(String name) {         if (name.equals(\"phone\")) {             entity.setPhone(null);         } else {             super.removeAttribute(name);         }     }      @Override     public void setAttribute(String name, List&lt;String&gt; values) {         if (name.equals(\"phone\")) {             entity.setPhone(values.get(0));         } else {             super.setAttribute(name, values);         }     }      @Override     public String getFirstAttribute(String name) {         if (name.equals(\"phone\")) {             return entity.getPhone();         } else {             return super.getFirstAttribute(name);         }     }      @Override     public Map&lt;String, List&lt;String&gt;&gt; getAttributes() {         Map&lt;String, List&lt;String&gt;&gt; attrs = super.getAttributes();         MultivaluedHashMap&lt;String, String&gt; all = new MultivaluedHashMap&lt;&gt;();         all.putAll(attrs);         all.add(\"phone\", entity.getPhone());         return all;     }      @Override     public Stream&lt;String&gt; getAttributeStream(String name) {         if (name.equals(\"phone\")) {             List&lt;String&gt; phone = new LinkedList&lt;&gt;();             phone.add(entity.getPhone());             return phone.stream();         } else {             return super.getAttributeStream(name);         }     } } <\/code><\/pre>\n<p>\u0420\u0438\u0441. 10 \u041a\u043b\u0430\u0441\u0441 <code>UserAdapter<\/code> \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f Keycloak<\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 <strong>UserAdapter<\/strong> \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 Keycloak \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438\u0445 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u043e, \u0447\u0442\u043e \u043e\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442\u0441\u044f \u043e\u0442 <strong>AbstractUserAdapterFederatedStorage<\/strong>, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430:<br \/> \u2022 \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f UserModel.<br \/> \u2022 \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0432 Federated Storage (\u0435\u0441\u043b\u0438 \u043d\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b).<br \/> \u2022 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u043d\u043e\u0433\u043e\u0437\u043d\u0430\u0447\u043d\u044b\u0445 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 (MultivaluedHashMap).<br \/> \u2022 \u041e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u044f\u0432\u043d\u043e.<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u044f \u0445\u043e\u0447\u0443 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u043e\u0441\u043e\u0431\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 keycloakId:<\/p>\n<pre><code class=\"java\">keycloakId = StorageId.keycloakId(model, entity.getId()); <\/code><\/pre>\n<p>\u0420\u0438\u0441. 11 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 Keycloak \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u043e\u0434\u0435\u043b\u0438<\/p>\n<p><strong>\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0432\u0430\u0436\u043d\u043e?<\/strong> Keycloak \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435<\/p>\n<pre><code class=\"java\">f:{providerId}:{externalId}, <\/code><\/pre>\n<p>\u0420\u0438\u0441. 12 \u0424\u043e\u0440\u043c\u0430\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<p>\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u0443\u044e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0437 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"java\">f:my-provider:123. <\/code><\/pre>\n<p>\u0420\u0438\u0441. 13 \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0444\u043e\u0440\u043c\u0430\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 Keycloak \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0438\u0437 \u0440\u0430\u0437\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432, \u0438 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u0442\u0430\u043a\u0436\u0435 \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0436\u0434\u0443 Keycloak \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u044f \u0440\u0438\u0441\u043a \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0446\u0435\u043b\u043e\u0441\u0442\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u0418 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0448\u0442\u0440\u0438\u0445, \u044d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0430\u0431\u0440\u0438\u043a\u0438.<br \/> \u0424\u0430\u0431\u0440\u0438\u043a\u0430 <strong>ExternalUserStorageProviderFactory<\/strong> \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430<\/strong>: \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <code>create<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>ExternalUserStorageProvider<\/code> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f<\/strong>: \u041c\u0435\u0442\u043e\u0434 <code>getId<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0435\u0433\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0432 Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/strong>: \u041c\u0435\u0442\u043e\u0434 <code>getHelpText<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u044e\u0449\u0435\u0435 \u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0444\u0430\u0431\u0440\u0438\u043a\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e Keycloak \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 keycloak. \u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430  \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<pre><code class=\"bash\">mvn clean install <\/code><\/pre>\n<p>\u0420\u0438\u0441. 14 \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u043b\u0430\u0433\u0438\u043d\u0430<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438, \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 target \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d  <strong>keycloak_api_plugin-1.0-SNAPSHOT.jar<\/strong><\/p>\n<p>\u042f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c keycloak  \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 docker \u043e\u0431\u0440\u0430\u0437\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u0435\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 compose \u0444\u0430\u0439\u043b \u0438 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0435\u0433\u043e \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code class=\"bash\">services:   keycloak_with_plugin:     image: quay.io\/keycloak\/keycloak:26.1.3     environment:       KEYCLOAK_ADMIN: admin       KEYCLOAK_ADMIN_PASSWORD: admin     ports:       - \"8080:8080\"     volumes:       - .\/target\/keycloak_api_plugin-1.0-SNAPSHOT.jar:\/opt\/keycloak\/providers\/keycloak_api_plugin-1.0-SNAPSHOT.jar     command: start-dev <\/code><\/pre>\n<p>\u0420\u0438\u0441. 15 docker-compose.yml \u0444\u0430\u0439\u043b<\/p>\n<p>\u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u0430\u0448\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e  **keycloak_api_plugin-1.0-SNAPSHOT.jar **\u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0441\u043e\u0431\u0440\u0430\u043d \u0437\u0430\u0440\u0430\u043d\u0435\u0435.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443<\/p>\n<pre><code class=\"bash\">docker compose up \u2014build  <\/code><\/pre>\n<p>\u0420\u0438\u0441. 16 \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 docker compose<\/p>\n<p>\u0438 \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0431\u0440\u0430\u043d, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443  <a href=\"http:\/\/0.0.0.0:8080\" rel=\"noopener noreferrer nofollow\">http:\/\/0.0.0.0:8080<\/a><\/p>\n<p>\u0432\u0432\u043e\u0434\u0438\u043c \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c <strong>admin admin<\/strong><\/p>\n<p>\u0417\u0430\u0442\u0435\u043c \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u0439\u0442\u0438 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b <strong>User Federation<\/strong> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d:<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/aa9\/8e1\/cba\/aa98e1cba815a4e119486d4af530b9e6.png\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/aa9\/8e1\/cba\/aa98e1cba815a4e119486d4af530b9e6.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/aa9\/8e1\/cba\/aa98e1cba815a4e119486d4af530b9e6.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0438\u0441. 17 \u0420\u0430\u0437\u0434\u0435\u043b <code>User Federation<\/code> \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0440\u0430\u0437\u0434\u0435\u043b <strong>Users<\/strong><\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/f68\/742\/1ac\/f687421acffe503f4080d4c69bd938f8.png\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/f68\/742\/1ac\/f687421acffe503f4080d4c69bd938f8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/f68\/742\/1ac\/f687421acffe503f4080d4c69bd938f8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0438\u0441. 18 \u0420\u0430\u0437\u0434\u0435\u043b <code>Users<\/code> \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/92c\/512\/351\/92c512351ba3ba6360a1bd2e3eb427f4.png\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/92c\/512\/351\/92c512351ba3ba6360a1bd2e3eb427f4.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/92c\/512\/351\/92c512351ba3ba6360a1bd2e3eb427f4.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0438\u0441. 19 \u041e\u0431\u0437\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/p>\n<p>\u041c\u044b \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b.<\/p>\n<p>\u0420\u0430\u0437\u043b\u043e\u0433\u0438\u043d\u0438\u043c\u0441\u044f \u0438 \u0437\u0430\u0439\u0434\u0435\u043c \u043f\u043e\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/6a5\/e3c\/29b\/6a5e3c29b4b2ccaa8546bc6cf13891ef.png\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/\/post_images\/6a5\/e3c\/29b\/6a5e3c29b4b2ccaa8546bc6cf13891ef.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/\/post_images\/6a5\/e3c\/29b\/6a5e3c29b4b2ccaa8546bc6cf13891ef.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0420\u0438\u0441. 20 \u041a\u0430\u0431\u0438\u043d\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 Keycloak<\/p>\n<p>\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e, \u0432\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0438\u043b\u0438\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u042d\u0442\u043e \u0431\u044b\u043b \u0434\u043e\u043b\u0433\u0438\u0439 \u043f\u0443\u0442\u044c, \u043e\u0434\u043d\u0430\u043a\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u043f\u043b\u0430\u0433\u0438\u043d \u0434\u043b\u044f Keycloak, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0435\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u043c API \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u044d\u0442\u0438\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438. \u042d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0443\u0436\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u0441\u0432\u043e\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u043d\u043e \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u0435\u0433\u043e \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 \u043a\u043b\u0430\u0441\u0441 <strong>ExternalUserStorageProvider<\/strong>, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c. \u0422\u0430\u043a\u0436\u0435 \u0441\u0442\u043e\u0438\u0442 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 <strong>UserAdapter<\/strong>, \u0432\u043d\u0435\u0434\u0440\u0438\u0432 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u0438\u043c. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 Keycloak, \u0447\u0442\u043e \u043e\u0442\u043a\u0440\u043e\u0435\u0442 \u043d\u043e\u0432\u044b\u0435 \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u044b \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043e\u0442\u043b\u0438\u0447\u043d\u0430\u044f \u043e\u0441\u043d\u043e\u0432\u0430 \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0438 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f, \u044f \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0438\u0442 \u0432\u0430\u0441 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0435\u0449\u0435 \u0431\u043e\u043b\u0435\u0435 \u043c\u043e\u0449\u043d\u044b\u0445 \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0432 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 Keycloak.<\/p>\n<p><strong>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0430\u0441\u044c<\/strong> \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f, \u0442\u043e \u0432\u044b \u0437\u043d\u0430\u0435\u0442\u0435 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c, \u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043b\u0430\u0439\u043a\u0438 \u0438 \u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0440\u0435\u043f\u043e\u0441\u0442\u044b. <strong>\u042d\u0442\u043e \u043b\u0443\u0447\u0448\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0430.<\/strong> \u0421 \u0432\u0430\u043c\u0438 \u0431\u044b\u043b \u0414\u0443\u0431\u043e\u0432\u0438\u0446\u043a\u0438\u0439 \u042e\u0440\u0438\u0439, \u0430\u0432\u0442\u043e\u0440 \u043a\u0430\u043d\u0430\u043b\u0430 \u00ab<a href=\"https:\/\/t.me\/notReadySir\" rel=\"noopener noreferrer nofollow\">\u0412\u0430\u0448 \u043a\u043e\u0434 \u0435\u0449\u0435 \u043d\u0435 \u0433\u043e\u0442\u043e\u0432, \u0441\u044d\u0440\u00bb.<\/a><br \/> \u0410 \u043d\u0430\u0448 \u043a\u043e\u0434 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432. \u0414\u043e \u0441\u0432\u044f\u0437\u0438!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/articles\/919230\/\"> https:\/\/habr.com\/ru\/articles\/919230\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><em>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0434\u043b\u044f \u043b\u044e\u0434\u0435\u0439, \u0438\u043c\u0435\u044e\u0449\u0438\u0445 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u043f\u044b\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 java \u0438 \u0442\u0435\u0445, \u043a\u0442\u043e \u0445\u043e\u0442\u044f \u0431\u044b \u0447\u0442\u043e-\u0442\u043e \u0441\u043b\u044b\u0448\u0430\u043b \u043e <\/em><a href=\"https:\/\/www.keycloak.org\/\" rel=\"noopener noreferrer nofollow\"><em>Keycloak<\/em><\/a><em>. \u0412 \u043d\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043d\u043e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u043b\u0430\u0433\u0438\u043d, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0440\u0430\u0431\u043e\u0430\u0442\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 API \u0441\u0435\u0440\u0432\u0438\u0441. \u042f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0442\u0435\u043c\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u044f\u0437\u044b\u043a\u043e\u043c \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435.<\/em><\/p>\n<p>\u041a\u043e\u043b\u043b\u0435\u0433\u0438, \u043d\u0430 \u0441\u0432\u044f\u0437\u0438 <a href=\"https:\/\/t.me\/notReadySir\" rel=\"noopener noreferrer nofollow\">\u0412\u0430\u0448 \u043a\u043e\u0434 \u0435\u0449\u0435 \u043d\u0435 \u0433\u043e\u0442\u043e\u0432, \u0441\u044d\u0440!<\/a> \u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e Keycloak, \u0430 \u0442\u043e\u0447\u043d\u0435\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443.<\/p>\n<p>\u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u0432\u043e\u043f\u0440\u043e\u0441, \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 Keycloak? \u041f\u043e \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438, \u0432\u043e\u0437\u044c\u043c\u0443 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0438\u0437 \u0412\u0438\u043a\u0438.<\/p>\n<blockquote>\n<p>Keycloak \u2014 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a OAuth 2.0 \u0438 OpenID Connect, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044f \u0435\u0434\u0438\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 (SSO) \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/blockquote>\n<p>\u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0430\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0447\u0435\u0442\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0432\u0441\u0435, \u0447\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u0430\u043d\u043d\u0443\u044e \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e, \u043d\u043e \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u0430\u0432\u0442\u043e\u0440\u044b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0438 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442. \u041a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0432 <strong>\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<blockquote>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c SPI \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (User Storage SPI), \u0447\u0442\u043e\u0431\u044b \u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f Keycloak, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0431\u0430\u0437\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430\u043c \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 LDAP \u0438 Active Directory \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u043e\u0433\u043e SPI \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Keycloak \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0432\u043e\u044e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0434\u043d\u0430\u043a\u043e \u0447\u0430\u0441\u0442\u043e \u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u043f\u0440\u0438\u0435\u0442\u0430\u0440\u043d\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043d\u0438 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u043c\u0438\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 Keycloak. \u0412 \u0442\u0430\u043a\u0438\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 User Storage SPI, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0432\u043d\u0435\u0448\u043d\u0435\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e Keycloak \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u0445 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c Keycloak \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0441\u0432\u043e\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0432\u0441\u0435, \u0447\u0442\u043e  \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432. \u0421\u043f\u0438\u0441\u043e\u043a \u044d\u0442\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0438 \u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u044b \u043d\u0438\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u043e\u0439\u0434\u0435\u043c \u0434\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<p>\u0410 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0438 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u042f \u043f\u043e\u043b\u0430\u0433\u0430\u044e, \u0447\u0442\u043e \u043d\u0430 \u0432\u0430\u0448\u0435\u0439 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u0435 \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b java, maven \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"bash\">mvn archetype:generate -DgroupId=org.example -DartifactId=keycloak-api-plugin -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false <\/code><\/pre>\n<p>\u0420\u0438\u0441. 1 \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0433\u043e maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<p>\u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u043c <strong>pom.xml<\/strong> \u0444\u0430\u0439\u043b:<\/p>\n<pre><code class=\"xml\">&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"   xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 http:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;   &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;    &lt;groupId&gt;org.example&lt;\/groupId&gt;   &lt;artifactId&gt;keycloak_api_plugin&lt;\/artifactId&gt;   &lt;version&gt;1.0-SNAPSHOT&lt;\/version&gt;   &lt;packaging&gt;jar&lt;\/packaging&gt;    &lt;name&gt;keycloak_api_plugin&lt;\/name&gt;   &lt;url&gt;http:\/\/maven.apache.org&lt;\/url&gt;    &lt;properties&gt;     &lt;keycloak.version&gt;26.2.2&lt;\/keycloak.version&gt;     &lt;postgresql.version&gt;42.7.3&lt;\/postgresql.version&gt;     &lt;maven.compiler.source&gt;17&lt;\/maven.compiler.source&gt;     &lt;maven.compiler.target&gt;17&lt;\/maven.compiler.target&gt;     &lt;project.build.sourceEncoding&gt;UTF-8&lt;\/project.build.sourceEncoding&gt;   &lt;\/properties&gt;    &lt;repositories&gt;     &lt;repository&gt;       &lt;id&gt;keycloak-releases&lt;\/id&gt;       &lt;url&gt;https:\/\/s01.oss.sonatype.org\/content\/repositories\/releases&lt;\/url&gt;     &lt;\/repository&gt;   &lt;\/repositories&gt;    &lt;dependencies&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-core&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;       &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;       &lt;version&gt;2.13.4.2&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-server-spi&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-server-spi-private&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.keycloak&lt;\/groupId&gt;       &lt;artifactId&gt;keycloak-services&lt;\/artifactId&gt;       &lt;version&gt;${keycloak.version}&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;javax.ws.rs&lt;\/groupId&gt;       &lt;artifactId&gt;javax.ws.rs-api&lt;\/artifactId&gt;       &lt;version&gt;2.1&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;       &lt;artifactId&gt;jackson-annotations&lt;\/artifactId&gt;       &lt;version&gt;2.13.3&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.jboss.resteasy&lt;\/groupId&gt;       &lt;artifactId&gt;resteasy-client&lt;\/artifactId&gt;       &lt;version&gt;3.15.1.Final&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;       &lt;artifactId&gt;lombok&lt;\/artifactId&gt;       &lt;version&gt;1.18.38&lt;\/version&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.jboss.resteasy&lt;\/groupId&gt;       &lt;artifactId&gt;resteasy-jackson2-provider&lt;\/artifactId&gt;       &lt;version&gt;3.15.1.Final&lt;\/version&gt;       &lt;scope&gt;provided&lt;\/scope&gt;     &lt;\/dependency&gt;     &lt;dependency&gt;       &lt;groupId&gt;org.mindrot&lt;\/groupId&gt;       &lt;artifactId&gt;jbcrypt&lt;\/artifactId&gt;       &lt;version&gt;0.4&lt;\/version&gt;     &lt;\/dependency&gt;   &lt;\/dependencies&gt;   &lt;build&gt;     &lt;plugins&gt;       &lt;plugin&gt;         &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;         &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt;         &lt;version&gt;3.11.0&lt;\/version&gt;         &lt;configuration&gt;           &lt;source&gt;${maven.compiler.source}&lt;\/source&gt;           &lt;target&gt;${maven.compiler.target}&lt;\/target&gt;         &lt;\/configuration&gt;       &lt;\/plugin&gt;       &lt;plugin&gt;         &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;         &lt;artifactId&gt;maven-shade-plugin&lt;\/artifactId&gt;         &lt;version&gt;3.5.1&lt;\/version&gt;         &lt;executions&gt;           &lt;execution&gt;             &lt;phase&gt;package&lt;\/phase&gt;             &lt;goals&gt;               &lt;goal&gt;shade&lt;\/goal&gt;             &lt;\/goals&gt;             &lt;configuration&gt;               &lt;artifactSet&gt;                 &lt;includes&gt;                   &lt;include&gt;org.mindrot:jbcrypt&lt;\/include&gt;                 &lt;\/includes&gt;               &lt;\/artifactSet&gt;             &lt;\/configuration&gt;           &lt;\/execution&gt;         &lt;\/executions&gt;       &lt;\/plugin&gt;     &lt;\/plugins&gt;   &lt;\/build&gt; &lt;\/project&gt; <\/code><\/pre>\n<p>\u0420\u0438\u0441. 2 \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c POM-\u0444\u0430\u0439\u043b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Keycloak, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a keycloak-core, keycloak-server-spi, keycloak-server-spi-private \u0438 keycloak-services, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JSON, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a jackson-databind \u0438 jackson-annotations, \u0430 \u0442\u0430\u043a\u0436\u0435 JAX-RS \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0432\u043a\u043b\u044e\u0447\u0430\u044f resteasy-client \u0438 resteasy-jackson2-provider. \u0412\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a Keycloak, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0440\u0441\u0438\u044e, \u0447\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.<\/p>\n<pre><code>keycloak-api-plugin \u2502 \u251c\u2500\u2500 pom.xml \u2502 \u2514\u2500\u2500 src     \u2514\u2500\u2500 main         \u2514\u2500\u2500 java             \u2514\u2500\u2500 org                 \u2514\u2500\u2500 example                     \u2514\u2500\u2500 keycloakapiplugin                         \u251c\u2500\u2500 adapter                         \u2502   \u2514\u2500\u2500 UserAdapter.java                         \u2502                         \u251c\u2500\u2500 api                         \u2502   \u2514\u2500\u2500 UsersApiService.java                         \u2502                         \u251c\u2500\u2500 dto                         \u2502   \u2514\u2500\u2500 UserResponseDto.java                         \u2502                         \u251c\u2500\u2500 entity                         \u2502   \u2514\u2500\u2500 UserEntity.java                         \u2502                         \u251c\u2500\u2500 mapper                         \u2502   \u2514\u2500\u2500 UserMapper.java                         \u2502                         \u251c\u2500\u2500 ExternalUserStorageProvider.java                         \u2502                         \u2514\u2500\u2500 ExternalUserStorageProviderFactory.java  <\/code><\/pre>\n<p>\u0420\u0438\u0441. 3 \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 maven \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438 \u0432\u044b\u044f\u0441\u043d\u0438\u043c, \u0437\u0430 \u0447\u0442\u043e \u043e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442.<\/p>\n<ol>\n<li>\n<p><strong>ExternalUserStorageProviderFactory<\/strong> &#8212; \u0424\u0430\u0431\u0440\u0438\u043a\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u0441\u043e\u0437\u0434\u0430\u044e\u0449\u0430\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b ExternalUserStorageProvider. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0438\u043c\u044f \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0434\u043b\u044f Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>ExternalUserStorageProvider<\/strong> &#8212; \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 (\u043f\u043e\u0438\u0441\u043a, \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0437\u0430\u043f\u0440\u043e\u0441\u044b). \u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>UserEntity<\/strong> &#8212; \u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0430\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f (id, username, email \u0438 \u0442.\u0434.). \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>UserResponseDto<\/strong> &#8212; DTO \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e API. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 JSON.<\/p>\n<\/li>\n<li>\n<p><strong>UsersApiService<\/strong> &#8212; \u0421\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c API \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0447\u0435\u0440\u0435\u0437 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\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 \u0440\u0430\u0437\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>UserAdapter<\/strong> &#8212; \u0410\u0434\u0430\u043f\u0442\u0435\u0440, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0438\u0439 UserEntity \u0432 \u043c\u043e\u0434\u0435\u043b\u044c Keycloak. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f Keycloak.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432 \u043e\u0431\u0449\u0438\u0445 \u0447\u0435\u0440\u0442\u0430\u0445 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u043a\u0430\u043a\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0437\u0430 \u0447\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0430\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0430 <strong>User Storage SPI.<\/strong><\/p>\n<blockquote>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0441\u0440\u0435\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Keycloak \u043d\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u0438\u0441\u043a\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u043e\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0440\u044f\u0434 \u0448\u0430\u0433\u043e\u0432 \u0434\u043b\u044f \u0435\u0433\u043e<\/p>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/div>\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-463658","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/463658","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=463658"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/463658\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=463658"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=463658"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=463658"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}