{"id":459209,"date":"2025-05-12T03:04:13","date_gmt":"2025-05-12T03:04:13","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=459209"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=459209","title":{"rendered":"<span>FastAPI + Keycloak: \u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/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>\u0414\u0440\u0443\u0437\u044c\u044f, \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u0414\u0430\u0432\u043d\u043e \u0445\u043e\u0442\u0435\u043b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 open-source \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u043a\u0430\u043a Keycloak \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u044f\u0437\u044b\u043a\u043e\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u043e\u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u0430\u0436\u0435 \u0432 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445.<\/p>\n<p>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u044f \u0431\u0443\u0434\u0443 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0432\u044f\u0437\u043a\u0435 Keycloak + FastAPI (Python), \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0442\u043e \u0434\u0430\u043d\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u0432\u0430\u0441 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Keycloak \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b.<\/p>\n<h3>\u041e \u0447\u0435\u043c \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f?<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u044f\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0431\u0438\u0442\u0430 \u043d\u0430 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0431\u043b\u043e\u043a\u0430:<\/p>\n<ol>\n<li>\n<p><strong>\u0422\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/strong>\u00a0\u2013 \u0437\u0434\u0435\u0441\u044c \u044f \u043a\u043e\u0440\u043e\u0442\u043a\u043e \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043f\u0440\u043e Keycloak \u0438 \u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u0445 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/strong>\u00a0\u2013 \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 CRUD-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0437\u0430\u043c\u0435\u0442\u043e\u043a. \u0425\u043e\u0442\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u043e\u0439, \u043d\u0430\u0448\u0430 \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u0446\u0435\u043b\u044c \u2013 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Keycloak.<\/p>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430: \u043f\u043e\u043f\u0430\u0434\u0430\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432\u0448\u0438\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u0430 \u0444\u043e\u0440\u043c\u0443 \u0432\u0445\u043e\u0434\u0430\/\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 Keycloak \u201c\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u201d \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u0434 \u0441\u0432\u043e\u0438\u043c \u043b\u043e\u0433\u0438\u043d\u043e\u043c \u0438 \u043f\u0430\u0440\u043e\u043b\u0435\u043c, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043f\u0430\u0434\u0430\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043c\u0435\u0442\u043e\u043a.<\/p>\n<p>\u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c:\u00a0<strong>\u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0444\u043e\u0440\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u043f\u043e \u043f\u043e\u0447\u0442\u0435, \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043d\u0430 email \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435<\/strong>, \u0432\u0435\u0434\u044c \u044d\u0442\u043e \u0432\u0441\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 Keycloak &#8212; \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432 \u0441\u0432\u043e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/057\/8a2\/269\/0578a2269dd7ce846a602d44f37df674.png\" alt=\"\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0432\u0445\u043e\u0434\u0430\" title=\"\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0432\u0445\u043e\u0434\u0430\" width=\"959\" height=\"790\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/057\/8a2\/269\/0578a2269dd7ce846a602d44f37df674.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/057\/8a2\/269\/0578a2269dd7ce846a602d44f37df674.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0432\u0445\u043e\u0434\u0430<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/db5\/896\/602\/db5896602c712ca010ceff4e62215a84.png\" alt=\"\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438\" title=\"\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438\" width=\"875\" height=\"906\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/db5\/896\/602\/db5896602c712ca010ceff4e62215a84.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/db5\/896\/602\/db5896602c712ca010ceff4e62215a84.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u043b\u0430\u043d \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c Keycloak<\/strong>\u00a0\u0441 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 (\u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Keycloak \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a> \u2013 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0437\u0430\u0439\u043c\u0435\u0442 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442, \u0430 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445)<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>\u00a0\u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043d\u0435 \u043f\u0443\u0442\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 Keycloak)<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c API<\/strong>\u00a0\u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0442\u043a\u0430\u043c\u0438: \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043c\u0435\u0442\u043e\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434<\/strong>\u00a0\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Jinja2, HTML, CSS \u0438 JavaScript<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442<\/strong>\u00a0\u043d\u0430 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a> (\u0437\u0430\u0439\u043c\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0440\u0443 \u043c\u0438\u043d\u0443\u0442)<\/p>\n<\/li>\n<\/ol>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u0435\u0441\u044c \u0441 Keycloak, \u044f \u0443\u0432\u0435\u0440\u0435\u043d, \u0447\u0442\u043e \u0432\u044b \u043d\u0430\u0447\u043d\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u0432 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/p>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Keycloak?<\/h3>\n<p>Keycloak \u2013 \u044d\u0442\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \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 (Identity and Access Management, IAM). \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 Keycloak \u2013 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0435\u0434\u0438\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 (Single Sign-On, SSO), \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h4>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438<\/h4>\n<p>Keycloak \u2013 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0432\u0441\u044e \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e:<\/p>\n<ul>\n<li>\n<p>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044e \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f)<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0449\u0438\u0442\u0435 API \u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u043d\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. Keycloak \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0442\u0438, \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432 (LDAP\/Active Directory) \u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0434\u0440\u0443\u0433\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439.<\/p>\n<h4>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 Keycloak<\/h4>\n<ul>\n<li>\n<p><strong>\u0415\u0434\u0438\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 (SSO)<\/strong>\u00a0\u2013 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432\u0445\u043e\u0434\u044f\u0442 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u043e \u0432\u0441\u0435\u043c \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c<\/p>\n<\/li>\n<li>\n<p><strong>\u0426\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong>\u00a0\u2013 \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0440\u043e\u043b\u0438 \u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0443\u044e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0430\u043d\u0435\u043b\u044c<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u043e\u0442\u043e\u0432\u044b\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/strong>\u00a0\u2013 \u0444\u043e\u0440\u043c\u044b \u0432\u0445\u043e\u0434\u0430, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u0443\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u0432<\/strong>\u00a0\u2013 \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441 OpenID Connect, OAuth 2.0 \u0438 SAML 2.0<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0435\u0430\u043b\u043c\u044b (Realms)<\/strong>\u00a0\u2013 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/strong>\u00a0\u2013 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b \u0434\u043b\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0438\u0437\u043a\u0438\u0439 \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0434\u0430<\/strong>\u00a0\u2013 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0434\u0430\u0436\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0435\u0437 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0430<\/strong>\u00a0\u2013 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u0434 \u0432\u0430\u0448 \u0431\u0440\u0435\u043d\u0434 (\u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u043c)<\/p>\n<\/li>\n<\/ul>\n<h4>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Keycloak<\/h4>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Keycloak \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u0435\u0440\u0432\u0435\u0440 Keycloak<\/strong>\u00a0\u2013 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0434\u0430\u043f\u0442\u0435\u0440\u044b Keycloak<\/strong>\u00a0\u2013 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0438\u043b\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u0432\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c Keycloak (\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0440\u0435\u0447\u044c \u043f\u0440\u043e FastAPI)<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<ol>\n<li>\n<p><strong>\u0417\u0430\u043f\u0440\u043e\u0441 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u0430<\/strong>\u00a0\u2014 \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u0441\u0443\u0440\u0441\u0443 \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 Keycloak<\/strong>\u00a0\u2014 \u0410\u0434\u0430\u043f\u0442\u0435\u0440 Keycloak \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d, \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 Keycloak \u0434\u043b\u044f \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0432\u043e\u0434 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>\u00a0\u2014 \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 Keycloak, \u0433\u0434\u0435 \u0432\u0432\u043e\u0434\u0438\u0442 \u0441\u0432\u043e\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043b\u043e\u0433\u0438\u043d\/\u043f\u0430\u0440\u043e\u043b\u044c) \u0438\u043b\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 Google \u0438\u043b\u0438 Facebook) \/ \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430<\/strong>\u00a0\u2014 \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 Keycloak \u043d\u0435 \u0441\u0440\u0430\u0437\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u044d\u0442\u0438\u043c \u043a\u043e\u0434\u043e\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u043c\u0435\u043d \u043a\u043e\u0434\u0430 \u043d\u0430 \u0442\u043e\u043a\u0435\u043d\u044b<\/strong>\u00a0\u2014 \u0412\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043a Keycloak \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u0430 \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u042d\u0442\u043e\u0442 \u0448\u0430\u0433 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u201c\u0437\u0430 \u043a\u0443\u043b\u0438\u0441\u0430\u043c\u0438\u201d \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u043c\u0438, \u0431\u0435\u0437 \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/strong>\u00a0\u2014 \u0412 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 Keycloak \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u0442\u0440\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u0430:<\/p>\n<ul>\n<li>\n<p><strong>access_token<\/strong>\u00a0\u2014 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c (\u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, 5-30 \u043c\u0438\u043d\u0443\u0442)<\/p>\n<\/li>\n<li>\n<p><strong>refresh_token<\/strong>\u00a0\u2014 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e access_token \u0431\u0435\u0437 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \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\u044f (\u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043e\u043b\u044c\u0448\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439)<\/p>\n<\/li>\n<li>\n<p><strong>id_token<\/strong>\u00a0\u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 (\u043f\u0440\u043e\u0444\u0438\u043b\u044c)<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c<\/strong>\u00a0\u2014 \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 access_token \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, Keycloak \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c\u0438, \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<h3>\u041a\u043e\u0433\u0434\u0430 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Keycloak?<\/h3>\n<p>Keycloak \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f\u0445:<\/p>\n<ol>\n<li>\n<p><strong>\u0412\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0449\u0443\u044e \u0431\u0430\u0437\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. Keycloak \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0430\u043c \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442 \u0435\u0434\u0438\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0432\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/strong>\u00a0&#8212; \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0442\u0438, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f, \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f email \u0438 \u0442.\u0434.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e. Keycloak \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430\u043c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438<\/strong>\u00a0&#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u043e\u043b\u0435\u0439 \u0438 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0447\u0435\u0440\u0435\u0437 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0435 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/strong>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (LDAP, Active Directory).<\/p>\n<\/li>\n<\/ol>\n<h3>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b Keycloak<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043c\u044b \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p><strong>Realm (\u0420\u0435\u0430\u043b\u043c)<\/strong>\u00a0\u2014 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u0440\u043e\u043b\u0438 \u0438 \u0433\u0440\u0443\u043f\u043f\u044b. \u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0430\u043b\u043c\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438\u043b\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>Client (\u041a\u043b\u0438\u0435\u043d\u0442)<\/strong>\u00a0\u2014 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0443 Keycloak. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0448\u0435 FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>User (\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c)<\/strong>\u00a0\u2014 \u0441\u0443\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>Role (\u0420\u043e\u043b\u044c)<\/strong>\u00a0\u2014 \u043d\u0430\u0431\u043e\u0440 \u043f\u0440\u0430\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c. \u0420\u043e\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u044b \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0438\u043b\u0438 \u0431\u044b\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u043c\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>Group (\u0413\u0440\u0443\u043f\u043f\u0430)<\/strong>\u00a0\u2014 \u043d\u0430\u0431\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0440\u043e\u043b\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Protocol (\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b)<\/strong>\u00a0\u2014 Keycloak \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f OpenID Connect (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 OAuth 2.0) \u0438 SAML 2.0. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c OpenID Connect.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 Keycloak \u0438 \u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0430.<\/p>\n<h3>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c Keycloak \u0441 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043b\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u043c\u043e\u0438\u043c \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u0442\u0432\u043e\u043c, \u0442\u043e \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u044f, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u0443\u044e\u0441\u044c \u0432 \u0441\u0442\u0430\u0442\u044c\u044f\u0445 \u043d\u0430 \u043d\u043e\u0432\u0438\u0447\u043a\u043e\u0432. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u0434\u0430\u043b\u0435\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0441\u0430\u043c\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u0435 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u0435\u043b\u043e\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0412 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0438, \u043a\u0430\u043a \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0434 Keycloak, \u0442\u0430\u043a \u0438 \u0441\u0430\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b Keycloak \u2013 \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a>.<\/p>\n<h4>\u0412\u0430\u0436\u043d\u043e \u043e \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/h4>\n<p>\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0422\u043e \u0435\u0441\u0442\u044c, \u043d\u0435 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0438 \u0434\u043b\u044f Keycloak. \u0414\u043b\u044f Keycloak \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 MySQL \u0438 PostgreSQL. \u041c\u044b \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c PostgreSQL.<\/p>\n<p>\u041f\u043e\u0434\u043d\u044f\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u043a\u0430\u043a \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430 VPS \u0441\u0435\u0440\u0432\u0435\u0440\u0435), \u043b\u0438\u0431\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 Amvera. \u0412\u044b\u0431\u0435\u0440\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u0440\u043e\u0449\u0435 \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0430\u0432\u044b\u043a\u043e\u0432.<\/p>\n<h4>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c PostgreSQL \u043d\u0430 Amvera Cloud<\/h4>\n<ol>\n<li>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera<\/a>, \u0435\u0441\u043b\u0438 \u0435\u0449\u0451 \u043d\u0435 \u0431\u044b\u043b\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0438 \u0432\u0445\u043e\u0434\u0438\u043c<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u00abPostgreSQL\u00bb<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445\u00bb<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c \u00ab\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u00bb, \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0442\u0430\u0440\u0438\u0444 (\u043d\u0435 \u043d\u0438\u0436\u0435 \u00ab\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439\u00bb)<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0421\u0423\u0411\u0414 PostgreSQL:<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u00bb \u0438 \u0434\u043e\u0436\u0438\u0434\u0430\u0435\u043c\u0441\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u0441\u0442\u0430\u0442\u0443\u0441 \u00abPostgreSQL \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u00bb.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/75f\/0ad\/07f\/75f0ad07f41faa5f3136947e322b2b87.png\" width=\"450\" height=\"122\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/75f\/0ad\/07f\/75f0ad07f41faa5f3136947e322b2b87.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/75f\/0ad\/07f\/75f0ad07f41faa5f3136947e322b2b87.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u042d\u0442\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f Keycloak. \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 &#8212; \u043e\u043d\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u044d\u0442\u0430\u043f\u0435.<\/p>\n<h4>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c \u0441\u0430\u043c Keycloak<\/h4>\n<p>\u0414\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f Keycloak \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0422\u0438\u043f: \u041f\u0440\u0435\u0434\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0437 \u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430: \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0422\u0438\u043f \u0441\u0435\u0440\u0432\u0438\u0441\u0430: Keycloak<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/470\/43e\/a5c\/47043ea5c0654edcc5c644045deff325.png\" width=\"876\" height=\"497\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/470\/43e\/a5c\/47043ea5c0654edcc5c644045deff325.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/470\/43e\/a5c\/47043ea5c0654edcc5c644045deff325.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0422\u0430\u0440\u0438\u0444: \u043d\u0435 \u043d\u0438\u0436\u0435 \u00ab\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439\u00bb \u0434\u043b\u044f \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5e4\/c06\/653\/5e4c06653fca991a94329a83fb9169e3.png\" width=\"875\" height=\"427\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/5e4\/c06\/653\/5e4c06653fca991a94329a83fb9169e3.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5e4\/c06\/653\/5e4c06653fca991a94329a83fb9169e3.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ol>\n<li>\n<p><strong>\u041d\u0430 \u044d\u0442\u0430\u043f\u0435 \u00ab\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u00bb \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f (envvars):<\/strong><\/p>\n<ul>\n<li>\n<p><code>KC_BOOTSTRAP_ADMIN_USERNAME<\/code>\u00a0\u2014 \u0438\u043c\u044f (login) \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/p>\n<\/li>\n<li>\n<p><code>KC_BOOTSTRAP_ADMIN_PASSWORD<\/code>\u00a0\u2014 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/p>\n<\/li>\n<li>\n<p><code>KC_DB<\/code>\u00a0\u2014 \u0442\u0438\u043f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<code>postgres<\/code>.<\/p>\n<\/li>\n<li>\n<p><code>KC_DB_URL_HOST<\/code>\u00a0\u2014 host \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (\u0442\u0443\u0442 \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f\/\u0437\u0430\u043f\u0438\u0441\u0438)<\/p>\n<\/li>\n<li>\n<p><code>KC_DB_URL_PORT<\/code>\u00a0\u2014 \u043f\u043e\u0440\u0442 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 5432 \u0434\u043b\u044f PostgreSQL, \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0438 \u043d\u0430 Amvera)<\/p>\n<\/li>\n<li>\n<p><code>KC_DB_URL_DATABASE<\/code>\u00a0\u2014 \u0438\u043c\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><code>KC_DB_USERNAME<\/code>\u00a0\u2014 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><code>KC_DB_PASSWORD<\/code>\u00a0\u2014 \u043f\u0430\u0440\u043e\u043b\u044c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u00bb \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0432\u043e\u0451<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e:<\/strong><\/p>\n<ul>\n<li>\n<p><code>KC_HOSTNAME<\/code>\u00a0\u2014 \u0432\u0430\u0448\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5ee\/547\/a2d\/5ee547a2dcd2b4948405b0eacddd9c67.png\" width=\"1551\" height=\"666\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/5ee\/547\/a2d\/5ee547a2dcd2b4948405b0eacddd9c67.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5ee\/547\/a2d\/5ee547a2dcd2b4948405b0eacddd9c67.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442. \u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u043f\u0440\u043e\u0448\u043b\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u043c\u0443 \u0438\u043c\u0435\u043d\u0438 \u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0442\u0430\u043a\u043e\u0435 \u043e\u043a\u043d\u043e:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8b0\/b40\/d56\/8b0b40d569962a2ed176c85571dba727.png\" width=\"1118\" height=\"865\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8b0\/b40\/d56\/8b0b40d569962a2ed176c85571dba727.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8b0\/b40\/d56\/8b0b40d569962a2ed176c85571dba727.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u0417\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u0430\u0434\u043c\u0438\u043d\u043a\u043e\u0439 Keycloak \u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0445\u043e\u0434<\/h3>\n<p>\u042d\u0442\u043e\u0439 \u0442\u0435\u043c\u0435 \u043b\u0435\u0433\u043a\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e, \u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f, \u043f\u0440\u043e\u0432\u0435\u0434\u0443 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439, \u043d\u043e \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u043d\u044b\u0439 \u044d\u043a\u0441\u043a\u0443\u0440\u0441.<\/p>\n<h4>\u0412\u0445\u043e\u0434 \u0432 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0430\u043d\u0435\u043b\u044c<\/h4>\n<p>\u0414\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0443 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439\u00a0<code>KC_HOSTNAME<\/code>. \u0412\u0430\u0441 \u0441\u0440\u0430\u0437\u0443 \u043f\u0435\u0440\u0435\u0431\u0440\u043e\u0441\u0438\u0442 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0412\u0432\u043e\u0434\u0438\u043c \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u2014 \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b, \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u043c\u0441\u044f \u0441 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u044c\u044e Keycloak.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3f1\/bc3\/ccf\/3f1bc3ccfe38cddd62d947993079c63b.png\" width=\"966\" height=\"675\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3f1\/bc3\/ccf\/3f1bc3ccfe38cddd62d947993079c63b.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3f1\/bc3\/ccf\/3f1bc3ccfe38cddd62d947993079c63b.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, Keycloak \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438\u0442 \u0432\u0430\u0441 \u043e\u0431 \u044d\u0442\u043e\u043c. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043d\u0430\u0434\u043e\u0435\u0434\u043b\u0438\u0432\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0443\u0434\u0430\u043b\u0438\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0443\u0447\u0435\u0442\u043a\u0443. \u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0430 \u0443\u0436\u0435 \u043f\u043e\u0442\u043e\u043c \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0440\u043e\u0433\u043e, \u0438\u043d\u0430\u0447\u0435 \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0435\u0441\u044c \u0431\u0435\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0433\u043e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430<\/h4>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Users<\/strong>, \u0437\u0430\u0442\u0435\u043c:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Add User<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b5f\/542\/4f9\/b5f5424f946150500b5d2df160fa52da.png\" width=\"1839\" height=\"651\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/b5f\/542\/4f9\/b5f5424f946150500b5d2df160fa52da.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b5f\/542\/4f9\/b5f5424f946150500b5d2df160fa52da.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (\u044d\u0442\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435). \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0442\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 email.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Create<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u0441\u044f \u0432 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435\u00a0<strong>Credentials<\/strong>\u00a0\u0437\u0430\u0434\u0430\u0451\u043c \u043f\u0430\u0440\u043e\u043b\u044c, \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u044f \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0444\u043b\u0430\u0433\u00a0<em>Temporary = OFF<\/em>.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Role mapping<\/strong>\u00a0\u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u0440\u043e\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 (<code>realm-admin<\/code>,\u00a0<code>admin<\/code>, \u0438 \u0442.\u0434.).<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b79\/95d\/c32\/b7995dc3266b4534f14e7269e6faa4c8.png\" width=\"1457\" height=\"637\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/b79\/95d\/c32\/b7995dc3266b4534f14e7269e6faa4c8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b79\/95d\/c32\/b7995dc3266b4534f14e7269e6faa4c8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043c\u0435\u043b\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c. \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0432\u044b\u043a\u0438\u043d\u0435\u0442 \u0432\u0430\u0441 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 \u2014 \u0432\u0432\u043e\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u043c \u043f\u0430\u0440\u043e\u043b\u044c, \u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0438\u043a\u0430\u043a\u0438\u0435 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u0430\u0441 \u043d\u0435 \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u044f\u0442.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Realm \u043f\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442<\/h3>\n<p>\u041a\u0430\u043a \u0443\u0436\u0435 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c,\u00a0<strong>Realm<\/strong>\u00a0\u0432 Keycloak \u2014 \u044d\u0442\u043e \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e, \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0436\u0438\u0432\u0451\u0442 \u0441\u0432\u043e\u0439 \u043d\u0430\u0431\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e. \u041c\u043e\u0436\u043d\u043e \u0443\u0441\u043b\u043e\u0432\u043d\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0435\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043c\u0438\u043d\u0438-\u0430\u0434\u043c\u0438\u043d\u043a\u043e\u0439 \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<p>\u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u043e\u043c\u00a0<strong>realm\u00a0<\/strong><code><strong>master<\/strong><\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u0434\u043c\u0438\u043d, \u0430 \u0434\u0430\u043b\u0435\u0435 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u043c\u044b. \u0422\u0430\u043a \u0438 \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u043c:<\/p>\n<ul>\n<li>\n<p>\u0412 \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u043b\u0435\u0432\u043e\u043c \u0443\u0433\u043b\u0443 \u043a\u043b\u0438\u043a\u0430\u0435\u043c \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0440\u0435\u0430\u043b\u043c\u0430 (<code>master<\/code>).<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c\u00a0<strong>Create realm<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/98e\/d70\/b5b\/98ed70b5bf794e51c48d435ecd48aea8.png\" width=\"361\" height=\"428\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/98e\/d70\/b5b\/98ed70b5bf794e51c48d435ecd48aea8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/98e\/d70\/b5b\/98ed70b5bf794e51c48d435ecd48aea8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0451\u043c \u0438\u043c\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440\u00a0<code>my-project<\/code>) \u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Create<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0432 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430<\/h3>\n<p>\u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u0438\u0446\u0443 \u043c\u0435\u0436\u0434\u0443\u00a0<strong>\u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438<\/strong>\u00a0\u0438\u00a0<strong>\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438<\/strong>. \u0412 Keycloak\u00a0<em>\u043a\u043b\u0438\u0435\u043d\u0442 (client)<\/em>\u00a0\u2014 \u044d\u0442\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 Keycloak. \u0410\u00a0<em>\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 (users)<\/em>\u00a0\u2014 \u044d\u0442\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0431\u0430\u0437\u0435.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Clients<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Create client<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e55\/4d5\/09c\/e554d509cbe557976a404ea31a905ff1.png\" width=\"1837\" height=\"660\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e55\/4d5\/09c\/e554d509cbe557976a404ea31a905ff1.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e55\/4d5\/09c\/e554d509cbe557976a404ea31a905ff1.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0412 \u043f\u043e\u043b\u0435\u00a0<strong>Client type<\/strong>\u00a0\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u00a0<code>OpenID Connect<\/code>\u00a0(\u043f\u0440\u043e SAML \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435).<\/p>\n<\/li>\n<li>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c\u00a0<strong>Client ID<\/strong>\u00a0\u2014 \u0438\u043c\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 \u043b\u0430\u0442\u0438\u043d\u0438\u0446\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440\u00a0<code>fastapi-client<\/code>. \u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Next<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0444\u043b\u0430\u0433\u00a0<strong>Client authentication<\/strong>, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u0416\u043c\u0451\u043c\u00a0<strong>Next<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u044d\u043a\u0440\u0430\u043d\u0435 \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u2014 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Save<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442 \u0441\u043e\u0437\u0434\u0430\u043d.<\/p>\n<h3>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, \u043d\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e)<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u043f\u0438\u0441\u044c\u043c\u0430 \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0447\u0442\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c SMTP-\u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0434\u0451\u043c \u0432\u00a0<strong>Realm settings \u2192 Email<\/strong>.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/137\/64d\/f56\/13764df569f9c18966edc4b62957fc45.png\" width=\"1801\" height=\"748\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/137\/64d\/f56\/13764df569f9c18966edc4b62957fc45.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/137\/64d\/f56\/13764df569f9c18966edc4b62957fc45.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u0443 Keycloak \u043d\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0439 SMTP. \u041c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, Yandex \u0438\u043b\u0438\u00a0<a href=\"http:\/\/Mail.ru\">Mail.ru<\/a>) \u0438\u043b\u0438 \u0430\u0440\u0435\u043d\u0434\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u043e\u0432\u0438\u043a \u043f\u043e\u0434 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0435\u0440\u0435\u0437\u00a0<a href=\"http:\/\/reg.ru\">reg.ru<\/a>).<\/p>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u00a0<strong>Yandex \u041f\u043e\u0447\u0442\u0443<\/strong>. \u0422\u0430\u043c \u043d\u0443\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043b\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439\u00a0<strong>\u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u043e\u043b\u044f:<\/p>\n<ul>\n<li>\n<p><strong>From<\/strong>\u00a0\u2014 \u0430\u0434\u0440\u0435\u0441, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043f\u0438\u0441\u044c\u043c\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440\u00a0<a href=\"mailto:yakvenalex@yandex.ru\"><code>yakvenalex@yandex.ru<\/code><\/a>).<\/p>\n<\/li>\n<li>\n<p><strong>From display name<\/strong>\u00a0\u2014 \u0438\u043c\u044f, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u0443 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440\u00a0<code>Mimo Support<\/code>).<\/p>\n<\/li>\n<li>\n<p><strong>Host<\/strong>\u00a0\u2014\u00a0<a href=\"http:\/\/smtp.yandex.ru\"><code>smtp.yandex.ru<\/code><\/a><\/p>\n<\/li>\n<li>\n<p><strong>Port<\/strong>\u00a0\u2014\u00a0<code>587<\/code><\/p>\n<\/li>\n<li>\n<p><strong>Enable StartTLS<\/strong>\u00a0\u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c<\/p>\n<\/li>\n<li>\n<p><strong>Enable Authentication<\/strong>\u00a0\u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432\u0432\u043e\u0434\u0438\u043c \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u043e\u0442 \u043f\u043e\u0447\u0442\u044b (\u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e\u00a0<a href=\"mailto:yakvenalex@yandex.ru\"><code>yakvenalex@yandex.ru<\/code><\/a>\u00a0\u0438 \u0440\u0430\u043d\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f).<\/p>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>Test<\/strong>\u00a0\u2014 \u0435\u0441\u043b\u0438 \u0432\u0441\u0451 \u0432\u0435\u0440\u043d\u043e, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u0438\u0441\u044c\u043c\u043e.<\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2d8\/dfc\/e42\/2d8dfce427d4269660133571935d6c5c.png\" width=\"431\" height=\"140\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/2d8\/dfc\/e42\/2d8dfce427d4269660133571935d6c5c.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2d8\/dfc\/e42\/2d8dfce427d4269660133571935d6c5c.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c email-\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e:<\/p>\n<ol>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Login<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u044b, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/111\/b75\/85d\/111b7585d0c9b6fe281c178a84e3462d.png\" width=\"417\" height=\"811\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/111\/b75\/85d\/111b7585d0c9b6fe281c178a84e3462d.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/111\/b75\/85d\/111b7585d0c9b6fe281c178a84e3462d.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e!<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h3>\n<p>\u041a \u0430\u0434\u043c\u0438\u043d\u043a\u0435 \u043c\u044b \u0435\u0449\u0451 \u0432\u0435\u0440\u043d\u0451\u043c\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u043e\u043c\u0435\u043d \u043d\u0430\u0448\u0435\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0410 \u043f\u043e\u043a\u0430 \u2014 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043a\u043e\u0434\u0443!<\/p>\n<h2>\u041f\u043e\u0434\u0433\u043e\u0442\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0430\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0441\u0443\u0434\u0438\u0442\u044c \u0441\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<h3>\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/h3>\n<h4>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0442\u0435\u043a \u043d\u0430 Python:<\/h4>\n<ul>\n<li>\n<p><strong>FastAPI<\/strong>\u00a0\u2014 \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u043b\u0443\u0447\u0448\u0438\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e backend\u2019\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>SQLAlchemy<\/strong>\u00a0\u2014 ORM \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>Alembic<\/strong>\u00a0\u2014 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Httpx<\/strong>\u00a0\u2014 HTTP-\u043a\u043b\u0438\u0435\u043d\u0442, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e FastAPI \u0431\u0443\u0434\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>Aiosqlite<\/strong>\u00a0\u2014 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a \u0434\u043b\u044f SQLite.<\/p>\n<\/li>\n<li>\n<p><strong>Jinja2<\/strong>\u00a0\u2014 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446 (\u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 CRUD-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b).<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0443:<\/h4>\n<p>\u042f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u043d\u0438\u043a\u0430\u043a\u0438\u0445 frontend-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u0412\u0441\u0451 \u043d\u0430\u00a0<strong>\u0447\u0438\u0441\u0442\u043e\u043c HTML + CSS + JS<\/strong>. \u0427\u0442\u043e\u0431\u044b \u043c\u0435\u043d\u044c\u0448\u0435 \u0432\u043e\u0437\u0438\u0442\u044c\u0441\u044f \u0441\u043e \u0441\u0442\u0438\u043b\u044f\u043c\u0438 \u0438 JavaScript \u2014 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u00a0<strong>Bootstrap 5<\/strong>.<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0438 \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430:<\/p>\n<ul>\n<li>\n<p><code>.env<\/code>\u00a0\u2014 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p><code>requirements.txt<\/code>\u00a0\u2014 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<\/ul>\n<h3>requirements.txt<\/h3>\n<pre><code class=\"css\">fastapi==0.115.12 uvicorn==0.34.2 httpx==0.28.1 loguru==0.7.3 pydantic==2.11.4 pydantic-settings==2.9.1 alembic==1.13.3 SQLAlchemy==2.0.35 aiosqlite==0.20.0 Jinja2==3.1.6<\/code><\/pre>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"bash\">pip install -r requirements.txt<\/code><\/pre>\n<h4>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c\u00a0<code>.env<\/code>\u00a0\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0443\u0434\u0430:<\/p>\n<pre><code class=\"css\">BASE_URL=\"\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0434\u043e\u043c\u0435\u043d \u0431\u044d\u043a\u0435\u043d\u0434\u0430\" KEYCLOAK_BASE_URL=\"\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0434\u043e\u043c\u0435\u043d Keycloak\" REALM=\"\u0438\u043c\u044f realm\" CLIENT_ID=\"\u0438\u043c\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430\" CLIENT_SECRET=\"\u0441\u0435\u043a\u0440\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"<\/code><\/pre>\n<h3>\u041f\u0440\u0438\u043c\u0435\u0440:<\/h3>\n<pre><code class=\"css\">BASE_URL=\"https:\/\/fastapikeycloack-yakvenalex.amvera.io\" KEYCLOAK_BASE_URL=\"https:\/\/authservice-yakvenalex.amvera.io\" REALM=\"fastapi-realm\" CLIENT_ID=\"fastapi-client\" CLIENT_SECRET=\"my_super_secret\"<\/code><\/pre>\n<h4>\u0413\u0434\u0435 \u0432\u0437\u044f\u0442\u044c CLIENT_SECRET \u0438 \u043a\u0430\u043a \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0431\u044d\u043a\u0435\u043d\u0434?<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0445\u043e\u0442\u0438\u0442\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u0441\u0451 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u043e\u0442\u00a0<strong>Ngrok<\/strong>,\u00a0<strong>Tuna<\/strong>\u00a0\u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u043e\u0440\u0442 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043f\u043e\u0440\u0442\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0430\u0448\u0435\u0433\u043e FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0435\u043a\u0440\u0435\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/h3>\n<ol>\n<li>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0443 Keycloak.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0443\u0436\u043d\u044b\u0439\u00a0<strong>client<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Credentials<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0422\u0430\u043c \u0443\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439\u00a0<code>secret<\/code>, \u043f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2fb\/30c\/b48\/2fb30cb485ac89d817b088b223937fb3.png\" width=\"1331\" height=\"771\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/2fb\/30c\/b48\/2fb30cb485ac89d817b088b223937fb3.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2fb\/30c\/b48\/2fb30cb485ac89d817b088b223937fb3.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/h3>\n<p>\u041f\u043e\u043a\u0430 \u0432\u044b \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435, \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>Settings<\/strong>\u00a0\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0442\u0443\u0434\u0430 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0430.<\/p>\n<p>\u041d\u0430 \u0447\u0442\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435:<\/p>\n<ul>\n<li>\n<p><strong>Web Origins<\/strong>\u00a0\u2014 \u044f \u0443\u043a\u0430\u0437\u0430\u043b\u00a0<code>*<\/code>, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0434\u043e\u043c\u0435\u043d\u0435. \u041d\u043e \u0432 \u0431\u043e\u0435\u0432\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0439\u0442\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0434\u043e\u043c\u0435\u043d\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>Valid redirect URIs<\/strong>\u00a0\u2014 \u043e\u0434\u043d\u0430 \u0438\u0437 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a. \u0418\u043c\u0435\u043d\u043d\u043e \u0441\u044e\u0434\u0430 Keycloak \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u043a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code>https:\/\/fastapikeycloack-yakvenalex.amvera.io\/api\/login\/callback<\/code><\/pre>\n<\/li>\n<\/ul>\n<p>\u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b, \u0438 \u043c\u044b \u043a \u043d\u0438\u043c \u0432\u0435\u0440\u043d\u0451\u043c\u0441\u044f \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c7a\/500\/1a3\/c7a5001a3c932f3100e4067c2944d07a.png\" width=\"960\" height=\"831\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/c7a\/500\/1a3\/c7a5001a3c932f3100e4067c2944d07a.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c7a\/500\/1a3\/c7a5001a3c932f3100e4067c2944d07a.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0430\u043f\u043a\u0443\u00a0<code>app\/<\/code>\u00a0\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0451 \u2014 \u0444\u0430\u0439\u043b\u00a0<a href=\"http:\/\/config.py\"><code>config.py<\/code><\/a>. \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"python\">import os from pydantic_settings import BaseSettings, SettingsConfigDict   class Settings(BaseSettings):     KEYCLOAK_BASE_URL: str     BASE_URL: str     REALM: str     CLIENT_ID: str     CLIENT_SECRET: str     BASE_DIR: str = os.path.abspath(os.path.join(os.path.dirname(__file__), \"..\"))      @property     def database_url(self) -&gt; str:         return f\"sqlite+aiosqlite:\/\/\/{self.BASE_DIR}\/data\/db.sqlite3\"      @property     def token_url(self) -&gt; str:         return f\"{self.KEYCLOAK_BASE_URL}\/realms\/{self.REALM}\/protocol\/openid-connect\/token\"      @property     def auth_url(self) -&gt; str:         return (             f\"{self.KEYCLOAK_BASE_URL}\/realms\/{self.REALM}\/protocol\/openid-connect\/auth\"         )      @property     def logout_url(self) -&gt; str:         return f\"{self.KEYCLOAK_BASE_URL}\/realms\/{self.REALM}\/protocol\/openid-connect\/logout\"      @property     def userinfo_url(self) -&gt; str:         return f\"{self.KEYCLOAK_BASE_URL}\/realms\/{self.REALM}\/protocol\/openid-connect\/userinfo\"      @property     def redirect_uri(self) -&gt; str:         return f\"{self.BASE_URL}\/api\/login\/callback\"      model_config = SettingsConfigDict(env_file=f\"{BASE_DIR}\/.env\")       settings = Settings() <\/code><\/pre>\n<h4>\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/h4>\n<ul>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u00a0<code>pydantic-settings<\/code>, \u0447\u0442\u043e\u0431\u044b \u0443\u0434\u043e\u0431\u043d\u043e \u0442\u044f\u043d\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u0437\u00a0<code>.env<\/code>.<\/p>\n<\/li>\n<li>\n<p><code>BASE_DIR<\/code>\u00a0\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u043e\u0433\u043e, \u043e\u0442\u043a\u0443\u0434\u0430 \u0432\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u044b \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0440\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u0441 Keycloak \u2014 \u0442\u043e\u043a\u0435\u043d\u044b, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435, logout \u0438 \u0442.\u0434.<\/p>\n<\/li>\n<li>\n<p><code>redirect_uri<\/code>\u00a0\u2014 \u044d\u0442\u043e endpoint, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Keycloak \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u0434 \u043f\u043e\u0441\u043b\u0435 \u043b\u043e\u0433\u0438\u043d\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d \u2014 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043b\u043e\u0433\u0438\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Keycloak!<\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c Keycloak \u0432 FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c\u0443 \u2014 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 FastAPI \u0441 Keycloak. \u0427\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0431\u044b\u043b\u043e \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u0438 \u0443\u0434\u043e\u0431\u043d\u043e, \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code class=\"python\">app\/ \u251c\u2500\u2500 services\/ \u2502   \u251c\u2500\u2500 keycloak_client.py \u2502   \u2514\u2500\u2500 auth_dep.py <\/code><\/pre>\n<h4>\u041e\u0431\u0449\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Keycloak<\/h4>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043a\u043e\u0434\u0443, \u043e\u0431\u044a\u044f\u0441\u043d\u044e, \u0447\u0442\u043e \u043c\u044b \u0432\u043e\u043e\u0431\u0449\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 Keycloak \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u0445\u043e\u0434\u0438\u0442, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0431\u043e\u0440 \u0442\u043e\u043a\u0435\u043d\u043e\u0432. \u0421\u0430\u043c\u043e\u0435 \u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0438 \u0442\u043e\u043a\u0435\u043d\u044b \u0432 cookie-\u0441\u0435\u0441\u0441\u0438\u0438. \u0410 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435:<\/p>\n<ol>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438\u00a0<code>access_token<\/code>\u00a0\u0432 cookie.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u0435\u0441\u0442\u044c \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0435\u0433\u043e \u043d\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c, \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044f\u00a0<code>userinfo<\/code>\u00a0\u0443 Keycloak.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u0436\u0438\u0432 \u0438 \u0432\u0430\u043b\u0438\u0434\u0435\u043d \u2014 \u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0442\u043e\u043a\u0435\u043d\u0430 \u043d\u0435\u0442 \u0438\u043b\u0438 \u043e\u043d \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u0435\u043d \u2014 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 Keycloak \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437\u00a0<code>httpx<\/code>.<\/p>\n<h3>keycloak_client.py\u00a0\u2014 \u043d\u0430\u0448 \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f Keycloak<\/h3>\n<pre><code class=\"python\">import httpx from fastapi import HTTPException  from app.config import settings  class KeycloakClient:     def __init__(self, client: httpx.AsyncClient | None = None):         self.client = client or httpx.AsyncClient()      async def get_tokens(self, code: str) -&gt; dict:         \"\"\"\u041e\u0431\u043c\u0435\u043d authorization code \u043d\u0430 \u0442\u043e\u043a\u0435\u043d\u044b\"\"\"         data = {             \"grant_type\": \"authorization_code\",             \"code\": code,             \"redirect_uri\": settings.redirect_uri,             \"client_id\": settings.CLIENT_ID,             \"client_secret\": settings.CLIENT_SECRET,         }         headers = {\"Content-Type\": \"application\/x-www-form-urlencoded\"}          try:             response = await self.client.post(                 settings.token_url, data=data, headers=headers             )             if response.status_code != 200:                 raise HTTPException(                     status_code=401, detail=f\"Token request failed: {response.text}\"                 )             return response.json()         except httpx.RequestError as e:             raise HTTPException(                 status_code=500, detail=f\"Token exchange failed: {str(e)}\"             )      async def get_user_info(self, token: str) -&gt; dict:         \"\"\"\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u043f\u043e access_token\"\"\"         headers = {\"Authorization\": f\"Bearer {token}\"}         try:             response = await self.client.get(settings.userinfo_url, headers=headers)             if response.status_code != 200:                 raise HTTPException(                     status_code=401, detail=f\"Invalid access token: {response.text}\"                 )             return response.json()         except httpx.RequestError as e:             raise HTTPException(                 status_code=500, detail=f\"Keycloak request error: {str(e)}\"             ) <\/code><\/pre>\n<h3>\u0420\u0430\u0437\u0431\u043e\u0440:<\/h3>\n<ul>\n<li>\n<p><code><strong>init<\/strong><\/code>: \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432\u043d\u0435\u0448\u043d\u0438\u0439\u00a0<code>httpx.AsyncClient<\/code>, \u043b\u0438\u0431\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0441\u0432\u043e\u0439. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<\/li>\n<li>\n<p><code>get_tokens<\/code>: \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u00a0<code>authorization_code<\/code>, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442 Keycloak, \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0440\u0438 \u0442\u043e\u043a\u0435\u043d\u0430:\u00a0<code>access_token<\/code>,\u00a0<code>refresh_token<\/code>,\u00a0<code>id_token<\/code>.<\/p>\n<\/li>\n<li>\n<p><code>get_user_info<\/code>: \u043f\u043e\u00a0<code>access_token<\/code>\u00a0\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u2014 \u044f\u0434\u0440\u043e, \u0432\u043e\u043a\u0440\u0443\u0433 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0440\u043e\u0438\u0442\u044c\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<h4>\u041a\u0430\u043a \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 Keycloak \u0441 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445?<\/h4>\n<p>\u0412\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 Keycloak \u0435\u0441\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 (UUID). \u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0432\u0445\u043e\u0434\u0435 \u043c\u043e\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u2014 \u0435\u0441\u0442\u044c \u043b\u0438 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0435\u0442 \u2014 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0435\u0441\u0442\u044c.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c,\u00a0<code>get_user_info<\/code>\u00a0\u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u043d\u043e \u0438 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c Keycloak-\u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u043a \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<h3>auth_dep.py\u00a0\u2014 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f FastAPI<\/h3>\n<pre><code class=\"python\">from fastapi import Depends, HTTPException, Request from app.services.keycloak_client import KeycloakClient   #\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c KeycloakClient \u0438\u0437 app.state def get_keycloak_client(request: Request) -&gt; KeycloakClient:     return request.app.state.keycloak_client     #\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0438\u0437 cookie async def get_token_from_cookie(request: Request) -&gt; str | None:     return request.cookies.get(\"access_token\")     #\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0442\u043e\u043a\u0435\u043d\u0443 async def get_current_user(     token: str = Depends(get_token_from_cookie),     keycloak: KeycloakClient = Depends(get_keycloak_client), ) -&gt; dict:     if not token:         raise HTTPException(status_code=401, detail=\"Unauthorized: No access token\")      try:         user_info = await keycloak.get_user_info(token)         return user_info     except HTTPException:         raise HTTPException(status_code=401, detail=\"Invalid token\") <\/code><\/pre>\n<h4>\u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043f\u043e \u043a\u043e\u0434\u0443:<\/h4>\n<h3>get_keycloak_client<\/h3>\n<pre><code class=\"python\">def get_keycloak_client(request: Request) -&gt; KeycloakClient:     return request.app.state.keycloak_client <\/code><\/pre>\n<p>\u042d\u0442\u043e \u0442\u0430 \u0441\u0430\u043c\u0430\u044f \u00ab\u0444\u0438\u0448\u043a\u0430\u00bb FastAPI, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b:\u00a0<code>app.state<\/code>. \u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043e\u0434\u0438\u043d \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u043c \u0435\u0433\u043e \u0442\u0443\u0434\u0430. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u00a0<code>httpx.AsyncClient<\/code>\u00a0\u043c\u0435\u0436\u0434\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u2014 \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u044c \u043d\u0430 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.<\/p>\n<h3>get_token_from_cookie<\/h3>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0434\u043e\u0441\u0442\u0430\u0451\u043c\u00a0<code>access_token<\/code>\u00a0\u0438\u0437 cookie. \u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u043d\u0435\u0442 \u2014 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d.<\/p>\n<h3>get_current_user<\/h3>\n<p>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0432\u0441\u0451 \u0432\u043c\u0435\u0441\u0442\u0435:<\/p>\n<ul>\n<li>\n<p>\u0434\u043e\u0441\u0442\u0430\u0451\u043c \u0442\u043e\u043a\u0435\u043d,<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 Keycloak,<\/p>\n<\/li>\n<li>\n<p>\u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435, \u0435\u0441\u043b\u0438 \u0432\u0441\u0451 \u043e\u043a.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c 401-\u044e \u043e\u0448\u0438\u0431\u043a\u0443. \u041f\u043e\u0437\u0436\u0435 \u0432 \u0440\u043e\u0443\u0442\u0435\u0440\u0435 \u043c\u043e\u0436\u0435\u043c \u043d\u0430 \u044d\u0442\u0443 \u043e\u0448\u0438\u0431\u043a\u0443 \u043e\u0442\u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u043c \u043d\u0430\u00a0<code>\/login<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445: SQLAlchemy \u0438 \u0441\u0432\u043e\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441\u00a0Keycloak\u00a0\u0438\u00a0PostgreSQL\u00a0\u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 Keycloak \u0441\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043d\u0443\u0436\u043d\u044b\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u0445\u0435\u043c\u044b, \u0442\u043e \u0441 \u043d\u0430\u0448\u0435\u0439\u00a0\u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445\u00a0\u0442\u0430\u043a \u043d\u0435 \u0432\u044b\u0439\u0434\u0435\u0442. \u041d\u0430\u043c \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f\u00a0\u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u00a0\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0437\u0430\u043c\u0435\u0442\u043e\u043a, \u0430 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u0432 \u044d\u0442\u043e\u043c, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435,\u00a0SQLAlchemy 2.<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0441\u0442\u0430\u0442\u044c\u044f \u0438 \u0442\u0430\u043a \u0443\u0436\u0435 \u0441\u0442\u0430\u043b\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u044a\u0451\u043c\u043d\u043e\u0439, \u044f\u00a0<strong>\u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c<\/strong>\u00a0\u0432\u0441\u0451, \u0447\u0442\u043e \u0443\u0436\u0435 \u043d\u0435 \u0440\u0430\u0437 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430\u00a0<code>BaseDAO<\/code>, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Alembic \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b. \u0417\u0430 \u044d\u0442\u0438\u043c \u2014 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u043c\u043e\u0438 <a href=\"https:\/\/habr.com\/ru\/users\/yakvenalex\/articles\/\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/yakvenalex.ru\/ru\/sqlalchemy\">\u0441\u0435\u0440\u0438\u044e \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0439 \u043f\u043e SQLAlchemy \u0438 Alembic<\/a>. \u0417\u0434\u0435\u0441\u044c \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0443\u0442\u044c.<\/p>\n<h4>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 DAO<\/h4>\n<p>\u0412 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u0443\u044e \u043f\u0430\u043f\u043a\u0443\u00a0<code>dao<\/code>\u00a0\u0438 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u044b:<\/p>\n<ul>\n<li>\n<p><a href=\"http:\/\/base.py\"><code><strong>base.py<\/strong><\/code><\/a>\u00a0\u2014 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0441 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c\u0438.<\/p>\n<\/li>\n<li>\n<p><a href=\"http:\/\/dao.py\"><code><strong>dao.py<\/strong><\/code><\/a>\u00a0\u2014 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 DAO-\u043a\u043b\u0430\u0441\u0441\u044b, \u0443\u0434\u043e\u0431\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u044c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p><a href=\"http:\/\/database.py\"><code><strong>database.py<\/strong><\/code><\/a>\u00a0\u2014 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0431\u0430\u0437\u0435.<\/p>\n<\/li>\n<li>\n<p><a href=\"http:\/\/models.py\"><code><strong>models.py<\/strong><\/code><\/a>\u00a0\u2014 ORM-\u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<\/li>\n<\/ul>\n<h4>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u0435 (database.py)<\/h4>\n<pre><code class=\"python\">from datetime import datetime  from sqlalchemy import TIMESTAMP, func from sqlalchemy.ext.asyncio import (     AsyncAttrs,     AsyncSession,     async_sessionmaker,     create_async_engine, ) from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column  from app.config import settings   engine = create_async_engine(url=settings.database_url) async_session_maker = async_sessionmaker(     engine, class_=AsyncSession, expire_on_commit=False )   class Base(AsyncAttrs, DeclarativeBase):     __abstract__ = True      created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())     updated_at: Mapped[datetime] = mapped_column(         TIMESTAMP, server_default=func.now(), onupdate=func.now()     ) <\/code><\/pre>\n<p>\u041e\u0431\u044b\u0447\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0432\u0438\u0436\u043a\u0430 \u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0441\u0441\u0438\u0439. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u2014 \u0432\u044b\u043d\u043e\u0441\u0438\u043c\u00a0<code>created_at<\/code>\u00a0\u0438\u00a0<code>updated_at<\/code>\u00a0\u0432 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<h4>\u041c\u043e\u0434\u0435\u043b\u0438 (models.py)<\/h4>\n<pre><code class=\"python\">from sqlalchemy import ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship  from app.dao.database import Base   class User(Base):     __tablename__ = \"users\"      id: Mapped[str] = mapped_column(primary_key=True)     email: Mapped[str] = mapped_column(unique=True)     email_verified: Mapped[bool]     name: Mapped[str]     preferred_username: Mapped[str]     given_name: Mapped[str]     family_name: Mapped[str]     notes: Mapped[list[\"Note\"]] = relationship(back_populates=\"user\")       class Note(Base):     __tablename__ = \"notes\"      id: Mapped[int] = mapped_column(primary_key=True)     title: Mapped[str]     content: Mapped[str]     user_id: Mapped[str] = mapped_column(ForeignKey(\"users.id\"))     user: Mapped[\"User\"] = relationship(back_populates=\"notes\") <\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u0434\u0432\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:\u00a0<code>users<\/code>\u00a0\u0438\u00a0<code>notes<\/code>.<\/p>\n<ul>\n<li>\n<p><code>id<\/code>\u00a0\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u2014 \u0441\u0442\u0440\u043e\u043a\u0430. \u042d\u0442\u043e \u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0434\u043b\u044f SQLite, \u043d\u043e \u0432 \u0431\u043e\u0435\u0432\u043e\u043c PostgreSQL \u043b\u0443\u0447\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c UUID.<\/p>\n<\/li>\n<li>\n<p>\u0412\u00a0<code>User<\/code>\u00a0\u0445\u0440\u0430\u043d\u0438\u043c \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u043e\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u0434\u0430\u0451\u0442 Keycloak:\u00a0<code>email<\/code>, \u0438\u043c\u044f, \u0444\u0430\u043c\u0438\u043b\u0438\u044f \u0438 \u0442. \u0434.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u00a0<code>relationship<\/code>\u00a0\u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u0438\u0445 \u0437\u0430\u043c\u0435\u0442\u043a\u0430\u043c\u0438.<\/p>\n<\/li>\n<\/ul>\n<h4>\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u043e\u0434 \u0441\u0435\u0441\u0441\u0438\u044e (services\/dao_dep.py)<\/h4>\n<pre><code class=\"python\">from typing import AsyncGenerator from sqlalchemy.ext.asyncio import AsyncSession from app.dao.database import async_session_maker   async def get_session_with_commit() -&gt; AsyncGenerator[AsyncSession, None]:     async with async_session_maker() as session:         try:             yield session             await session.commit()         except Exception:             await session.rollback()             raise         finally:             await session.close()               async def get_session_without_commit() -&gt; AsyncGenerator[AsyncSession, None]:     async with async_session_maker() as session:         try:             yield session         except Exception:             await session.rollback()             raise         finally:             await session.close() <\/code><\/pre>\n<p>\u041d\u0438\u0447\u0435\u0433\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e \u2014 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u043e\u0434 FastAPI \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439, \u0441 \u0438 \u0431\u0435\u0437 \u0430\u0432\u0442\u043e-\u043a\u043e\u043c\u043c\u0438\u0442\u0430.<\/p>\n<h4>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 DAO (base.py)<\/h4>\n<pre><code class=\"python\">class BaseDAO(Generic[T]):     model: Type[T] = None  # type: ignore      def __init__(self, session: AsyncSession):         self._session = session         if self.model is None:             raise ValueError(\"\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u0430 \u0432 \u0434\u043e\u0447\u0435\u0440\u043d\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0435\")      async def find_one_or_none_by_id(self, data_id: int | str):         try:             query = select(self.model).filter_by(id=data_id)             result = await self._session.execute(query)             record = result.scalar_one_or_none()             log_message = f\"\u0417\u0430\u043f\u0438\u0441\u044c {self.model.__name__} \u0441 ID {data_id} {'\u043d\u0430\u0439\u0434\u0435\u043d\u0430' if record else '\u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430'}.\"             logger.info(log_message)             return record         except SQLAlchemyError as e:             logger.error(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441 ID {data_id}: {e}\")             raise <\/code><\/pre>\n<p>\u042d\u0442\u043e \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441. \u0422\u0443\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434\u00a0<code>find_one_or_none_by_id<\/code>, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u2014 \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438. \u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u043f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u044d\u0442\u043e\u0433\u043e \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430, \u0432\u044b \u043d\u0430\u0439\u0434\u0435\u0442\u0435 \u0432 \u043c\u043e\u0435\u043c \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u043a\u0430\u043d\u0430\u043b\u0435 &#171;<a href=\"https:\/\/t.me\/PythonPathMaster\">\u041b\u0435\u0433\u043a\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 Python<\/a>&#171;.<\/p>\n<h4>\u0414\u043e\u0447\u0435\u0440\u043d\u0438\u0435 DAO-\u043a\u043b\u0430\u0441\u0441\u044b (dao.py)<\/h4>\n<pre><code class=\"python\">from app.dao.base import BaseDAO from app.dao.models import Note, User   class UsersDAO(BaseDAO):     model = User       class NotesDAO(BaseDAO):     model = Note <\/code><\/pre>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u0434\u0430\u0451\u043c \u043c\u043e\u0434\u0435\u043b\u044c \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0451 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043e\u0442 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u041f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0441\u0435\u0441\u0441\u0438\u044e \u2014 \u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<h4>\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u0438<\/h4>\n<p>\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u2014 \u0447\u0435\u0440\u0435\u0437 Alembic. \u0417\u0434\u0435\u0441\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043d\u0435 \u0440\u0430\u0441\u043f\u0438\u0441\u044b\u0432\u0430\u044e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0440\u0430\u0441\u0442\u044f\u0433\u0438\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e. \u0412 \u043c\u043e\u0438\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f\u0445 \u044d\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e. \u0421 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u043f\u043e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 &#171;<a href=\"https:\/\/habr.com\/ru\/companies\/amvera\/articles\/863130\/\">\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u043b\u043e\u0433\u0430 \u043d\u0430 FastAPI \u0441 \u043d\u0443\u043b\u044f: JWT, Markdown \u0438 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0432\u0435\u0431-\u0434\u0438\u0437\u0430\u0439\u043d<\/a>&#171;.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0422\u0430\u0431\u043b\u0438\u0446\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u044b, DAO \u0433\u043e\u0442\u043e\u0432\u044b, \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0441\u0441\u0438\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u044b. \u041c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e API.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c API-\u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0445\u043e\u0434\u0430 \u0438 \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/h3>\n<p>\u0412 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 API-\u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u0438\u0445 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0438 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e. \u0424\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0447\u0430\u0441\u0442\u044c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0431\u043e\u043b\u0435\u0435 \u043a\u0440\u0430\u0442\u043a\u043e.<\/p>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0432 \u043f\u0430\u043f\u043a\u0435\u00a0<code>app<\/code>\u00a0\u043f\u0430\u043f\u043a\u0443\u00a0<code>api<\/code>, \u0438 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u043c \u0432\u043d\u0443\u0442\u0440\u044c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430:\u00a0<a href=\"http:\/\/route.py\"><code>route.py<\/code><\/a>\u00a0(\u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f API-\u043c\u0435\u0442\u043e\u0434\u043e\u0432) \u0438\u00a0<a href=\"http:\/\/schemas.py\"><code>schemas.py<\/code><\/a>\u00a0(\u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445).<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0444\u0430\u0439\u043b\u0430\u00a0<a href=\"http:\/\/schemas.py\"><code>schemas.py<\/code><\/a>:<\/p>\n<pre><code class=\"python\">from pydantic import BaseModel   class SUserId(BaseModel):     user_id: str       class AddUser(BaseModel):     id: str     email: str     email_verified: bool     name: str     preferred_username: str     given_name: str     family_name: str       class AddNote(BaseModel):     title: str     content: str       class AddNoteWithUserId(AddNote, SUserId):     pass <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0432 FastAPI. \u0414\u0430\u043b\u0435\u0435 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u043a\u0430\u043a \u044d\u0442\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u0445.<\/p>\n<p>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0444\u0430\u0439\u043b\u0443\u00a0<code>app\/api\/<\/code><a href=\"http:\/\/router.py\"><code>router.py<\/code><\/a>. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440:<\/p>\n<pre><code class=\"python\">from urllib.parse import urlencode from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import RedirectResponse from loguru import logger from sqlalchemy.ext.asyncio import AsyncSession from app.api.schemas import AddNote, AddNoteWithUserId, AddUser, SUserId from app.config import settings from app.dao.dao import NotesDAO, UsersDAO from app.services.auth_dep import get_current_user, get_keycloak_client from app.services.dao_dep import (     get_session_with_commit,     get_session_without_commit, ) from app.services.keycloak_client import KeycloakClient   router = APIRouter(prefix=\"\/api\", tags=[\"API\"]) <\/code><\/pre>\n<p>\u0414\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u00a0<code>loguru<\/code>. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u043e\u043d\u044f\u0442\u043d\u044b, \u0435\u0441\u043b\u0438 \u0432\u044b \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0447\u0438\u0442\u0430\u043b\u0438 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435 \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e \u0445\u043e\u0434\u0443.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430 \u2014 \u0442\u043e\u0433\u043e, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 Keycloak:<\/p>\n<pre><code class=\"python\">@router.get(\"\/login\/callback\", include_in_schema=False) async def login_callback(     code: str | None = None,     error: str | None = None,     error_description: str | None = None,     session: AsyncSession = Depends(get_session_with_commit),     keycloak: KeycloakClient = Depends(get_keycloak_client), ) -&gt; RedirectResponse:     \"\"\"     \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 callback \u043f\u043e\u0441\u043b\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 Keycloak.     \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u043e\u043a\u0435\u043d, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0411\u0414 (\u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e)     \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 cookie \u0441 \u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438. \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u043e\u0442 Keycloak.     \"\"\"     if error:         logger.error(f\"Keycloak error: {error}, description: {error_description}\")         raise HTTPException(status_code=401, detail=\"Authorization code is required\")      if not code:         raise HTTPException(status_code=401, detail=\"Authorization code is required\")      try:         # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u043e\u0442 Keycloak         token_data = await keycloak.get_tokens(code)         access_token = token_data.get(\"access_token\")         refresh_token = token_data.get(\"refresh_token\")         id_token = token_data.get(\"id_token\")          if not access_token:             raise HTTPException(status_code=401, detail=\"\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\")         if not refresh_token:             raise HTTPException(status_code=401, detail=\"Refresh token \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\")         if not id_token:             raise HTTPException(status_code=401, detail=\"ID token \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\")          # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435         user_info = await keycloak.get_user_info(access_token)         user_id = user_info.get(\"sub\")         if not user_id:             raise HTTPException(status_code=401, detail=\"ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\")          # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438         users_dao = UsersDAO(session)         user = await users_dao.find_one_or_none_by_id(user_id)         if not user and isinstance(user_info, dict):             user_info[\"id\"] = user_info.pop(\"sub\")             await users_dao.add(AddUser(**user_info))          # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 cookie \u0441 \u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438 \u0438 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442         response = RedirectResponse(url=\"\/protected\")         response.set_cookie(             key=\"access_token\",             value=access_token,             httponly=True,             secure=True,             samesite=\"lax\",             path=\"\/\",             max_age=token_data.get(\"expires_in\", 3600),         )         response.set_cookie(             key=\"refresh_token\",             value=refresh_token,             httponly=True,             secure=True,             samesite=\"lax\",             path=\"\/\",             max_age=token_data.get(\"refresh_expires_in\", 2592000),         )         response.set_cookie(             key=\"id_token\",             value=id_token,             httponly=True,             secure=True,             samesite=\"lax\",             path=\"\/\",             max_age=token_data.get(\"expires_in\", 3600),         )         logger.info(f\"User {user_id} logged in successfully\")         return response      except Exception as e:         logger.error(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 callback'\u0430 \u043b\u043e\u0433\u0438\u043d\u0430: {str(e)}\")         raise HTTPException(status_code=401, detail=\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438\") <\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 callback \u043e\u0442 Keycloak \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d\u044b,<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435,<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0411\u0414 (\u0435\u0441\u043b\u0438 \u043e\u043d \u043d\u043e\u0432\u044b\u0439),<\/p>\n<\/li>\n<li>\n<p>\u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043a\u0443\u043a\u0438 \u0441 \u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438,<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: \u044d\u0442\u043e\u00a0<code>GET<\/code>-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442, \u0430 \u043d\u0435\u00a0<code>POST<\/code>. \u042d\u0442\u043e \u0432\u0430\u0436\u043d\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u044d\u0442\u043e\u0442 \u0430\u0434\u0440\u0435\u0441 Keycloak \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u00a0<code>code<\/code>\u00a0\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<p>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u2014 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 Keycloak \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u043e\u0434\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u043e\u043b\u0433\u043e \u043d\u0435 \u0432\u0432\u043e\u0434\u0438\u043b \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b \u043f\u043e\u0447\u0442\u0443). \u0422\u0430\u043a\u043e\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0442\u043e\u0436\u0435 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d: \u0435\u0441\u043b\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0435\u0441\u0442\u044c\u00a0<code>error<\/code>, \u0438\u043b\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442\u00a0<code>code<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430 401.<\/p>\n<p>\u041f\u043e\u0437\u0436\u0435, \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a: \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043f\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<h4>\u042d\u0442\u0430\u043f 1 \u2014 \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/h4>\n<pre><code class=\"python\">token_data = await keycloak.get_tokens(code) access_token = token_data.get(\"access_token\") refresh_token = token_data.get(\"refresh_token\") id_token = token_data.get(\"id_token\") <\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0431\u044b\u043b \u043e\u043f\u0438\u0441\u0430\u043d \u0440\u0430\u043d\u0435\u0435: \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u0434 \u0432 Keycloak \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432 \u043e\u0442\u0432\u0435\u0442 \u0442\u0440\u0438 \u0442\u043e\u043a\u0435\u043d\u0430. \u0412\u0441\u0435 \u043e\u043d\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b, \u0438\u043d\u0430\u0447\u0435 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c\u00a0<code>HTTPException<\/code>.<\/p>\n<h4>\u042d\u0442\u0430\u043f 2 \u2014 \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435<\/h4>\n<pre><code class=\"python\">user_info = await keycloak.get_user_info(access_token) user_id = user_info.get(\"sub\") <\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u00a0<code>access_token<\/code>, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0421\u0430\u043c\u043e\u0435 \u0432\u0430\u0436\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u2014 \u044d\u0442\u043e\u00a0<code>sub<\/code>, \u043e\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0422\u0430\u043a\u0436\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f email, \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u044b \u0438 \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f.<\/p>\n<h4>\u042d\u0442\u0430\u043f 3 \u2014 \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/h4>\n<pre><code class=\"python\">users_dao = UsersDAO(session) user = await users_dao.find_one_or_none_by_id(user_id) if not user and isinstance(user_info, dict):     user_info[\"id\"] = user_info.pop(\"sub\")     await users_dao.add(AddUser(**user_info)) <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0442\u0430\u043a\u0438\u043c ID \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u2014 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0435\u0433\u043e \u0432 \u0431\u0430\u0437\u0435. \u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0445\u043e\u0434\u00a0<code>BaseDAO<\/code>.<\/p>\n<h4>\u042d\u0442\u0430\u043f 4 \u2014 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0432 \u043a\u0443\u043a\u0438 \u0438 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442<\/h4>\n<pre><code class=\"python\">response = RedirectResponse(url=\"\/protected\") response.set_cookie(...) logger.info(f\"User {user_id} logged in successfully\") return response <\/code><\/pre>\n<p>\u041a\u0443\u043a\u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438\u00a0<code>httponly<\/code>,\u00a0<code>secure<\/code>,\u00a0<code>samesite=\"lax\"<\/code>. \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 (<code>samesite<\/code>) \u0441\u0442\u043e\u0438\u0442 \u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0430\u00a0<code>\"none\"<\/code>, \u0435\u0441\u043b\u0438 \u0444\u0440\u043e\u043d\u0442 \u0438 \u0431\u044d\u043a \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0434\u043e\u043c\u0435\u043d\u0430\u0445, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c CORS-\u043f\u0440\u043e\u0431\u043b\u0435\u043c.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043a\u0443\u043a\u043e\u0432 \u2014 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043d\u0430 \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b:<\/p>\n<pre><code class=\"python\">@router.get(\"\/logout\", include_in_schema=False) async def logout(request: Request):     id_token = request.cookies.get(\"id_token\")     params = {         \"client_id\": settings.CLIENT_ID,         \"post_logout_redirect_uri\": settings.BASE_URL,     }     if id_token:         params[\"id_token_hint\"] = id_token      keycloak_logout_url = f\"{settings.logout_url}?{urlencode(params)}\"     response = RedirectResponse(url=keycloak_logout_url)     response.delete_cookie(         key=\"access_token\",         httponly=True,         secure=True,         samesite=\"lax\",         path=\"\/\",     )     response.delete_cookie(         key=\"id_token\",         httponly=True,         secure=True,         samesite=\"lax\",         path=\"\/\",     )     response.delete_cookie(         key=\"refresh_token\",         httponly=True,         secure=True,         samesite=\"lax\",         path=\"\/\",     )     return response <\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0434\u0435\u043b\u0430\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u00a0<code>id_token<\/code>\u00a0\u0438\u0437 \u043a\u0443\u043a\u043e\u0432,<\/p>\n<\/li>\n<li>\n<p>\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u0432\u044b\u0445\u043e\u0434\u0430 \u0432 Keycloak (logout endpoint),<\/p>\n<\/li>\n<li>\n<p>\u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u043a\u0443\u043a\u0438 (\u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435\u00a0<code>access_token<\/code>,\u00a0<code>refresh_token<\/code>\u00a0\u0438\u00a0<code>id_token<\/code>),<\/p>\n<\/li>\n<li>\n<p>\u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430\u00a0<code>post_logout_redirect_uri<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0430\u0436\u043d\u043e: \u043c\u044b \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u043a\u0443\u043a\u0438, \u043d\u043e \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u0441\u0435\u0441\u0441\u0438\u0438 Keycloak. \u042d\u0442\u043e \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u043a\u0430\u043a \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, \u0442\u0430\u043a \u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 Identity-\u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c API-\u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0437\u0430\u043c\u0435\u0442\u043e\u043a. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c API-\u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0430\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 CRUD-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u043c\u0435\u0442\u043e\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438:<\/p>\n<pre><code class=\"python\">@router.post(\"\/notes\") async def add_note(     note: AddNote,     user: dict = Depends(get_current_user),     session: AsyncSession = Depends(get_session_with_commit), ):     notes_dao = NotesDAO(session)     await notes_dao.add(AddNoteWithUserId(user_id=user[\"sub\"], **note.model_dump()))     return {\"status\": \"ok\", \"message\": \"Note added successfully\", \"note\": note} <\/code><\/pre>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u0441\u0430\u043c\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438, \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0432\u0435 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u043d\u0435\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 FastAPI:<\/p>\n<ul>\n<li>\n<p><code>get_current_user<\/code>\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0449\u0430\u044f \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c\u00a0<code>access_token<\/code>\u00a0\u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u041f\u043e \u0441\u0443\u0442\u0438, \u044d\u0442\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \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\u044f;<\/p>\n<\/li>\n<li>\n<p><code>get_session_with_commit<\/code>\u00a0\u2014 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0430\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e SQLAlchemy \u0441 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u043e\u043c (\u0442.\u0435. \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430).<\/p>\n<\/li>\n<\/ul>\n<p>\u0417\u0430\u0442\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u00a0<code>BaseDAO<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u043b \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445 \u0438 \u0432\u0438\u0434\u0435\u043e. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u2014 \u0437\u0430\u043c\u0435\u0442\u043a\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0431\u0430\u0437\u0443, \u0438 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u0437\u0430\u043c\u0435\u0442\u043e\u043a \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"python\">@router.get(\"\/notes\") async def get_notes(     user: dict = Depends(get_current_user),     session: AsyncSession = Depends(get_session_without_commit), ):     notes_dao = NotesDAO(session)     notes = await notes_dao.find_all(SUserId(user_id=user[\"sub\"]))     return {\"status\": \"ok\", \"notes\": notes} <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0441\u043d\u043e\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \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\u044f \u0438 \u0441\u0435\u0441\u0441\u0438\u044e \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u043c\u043c\u0438\u0442\u0430 (<code>get_session_without_commit<\/code>), \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0438\u0442\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435 \u0432\u043d\u043e\u0441\u0438\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0442\u043a\u0438:<\/p>\n<pre><code class=\"python\">@router.delete(\"\/notes\/{note_id}\") async def delete_note(     note_id: str,     user: dict = Depends(get_current_user),     session: AsyncSession = Depends(get_session_with_commit), ):     notes_dao = NotesDAO(session)     note = await notes_dao.find_one_or_none_by_id(note_id)     if not note:         raise HTTPException(status_code=404, detail=\"\u0417\u0430\u043c\u0435\u0442\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430\")     if note.user_id != user[\"sub\"]:         raise HTTPException(             status_code=403, detail=\"\u0423 \u0432\u0430\u0441 \u043d\u0435\u0442 \u043f\u0440\u0430\u0432 \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438\"         )     await session.delete(note)     return {\"status\": \"ok\", \"message\": \"\u0417\u0430\u043c\u0435\u0442\u043a\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0430\"} <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043a\u0430\u043a DAO-\u043f\u043e\u0434\u0445\u043e\u0434, \u0442\u0430\u043a \u0438 \u00ab\u0447\u0438\u0441\u0442\u044b\u0435\u00bb \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 SQLAlchemy. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0449\u0435\u043c \u0437\u0430\u043c\u0435\u0442\u043a\u0443 \u043f\u043e ID, \u0430 \u0437\u0430\u0442\u0435\u043c, \u0435\u0441\u043b\u0438 \u0432\u0441\u0451 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0435\u0451 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0441\u0441\u0438\u044e.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435:<\/p>\n<ul>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0437\u0430\u043c\u0435\u0442\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f 404,<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0447\u0443\u0436\u0443\u044e \u0437\u0430\u043c\u0435\u0442\u043a\u0443 \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f 403.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0442\u043a\u0438:<\/p>\n<pre><code class=\"python\">@router.put(\"\/notes\/{note_id}\") async def update_note(     note_id: int,     note: AddNote,     user: dict = Depends(get_current_user),     session: AsyncSession = Depends(get_session_with_commit), ):     notes_dao = NotesDAO(session)     existing_note = await notes_dao.find_one_or_none_by_id(note_id)     if not existing_note:         raise HTTPException(status_code=404, detail=\"\u0417\u0430\u043c\u0435\u0442\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430\")     if existing_note.user_id != user[\"sub\"]:         raise HTTPException(             status_code=403, detail=\"\u0423 \u0432\u0430\u0441 \u043d\u0435\u0442 \u043f\u0440\u0430\u0432 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438\"         )     existing_note.title = note.title     existing_note.content = note.content     return {\"status\": \"ok\", \"message\": \"\u0417\u0430\u043c\u0435\u0442\u043a\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430\", \"note\": note} <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0441\u043d\u043e\u0432\u0430 \u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u00a0<code>BaseDAO<\/code>\u00a0\u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c SQLAlchemy. \u041c\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0437\u0430\u043c\u0435\u0442\u043a\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044f \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f API-\u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0433\u043b\u0430\u0432\u0435 \u0437\u0430\u0439\u043c\u0451\u043c\u0441\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0442\u0430\u0442\u044c\u044f \u0438 \u0442\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u044a\u0451\u043c\u043d\u043e\u0439, \u0430 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0431\u043e\u0440 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0435\u0451 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0435\u043b\u044c\u044e, \u0437\u0434\u0435\u0441\u044c \u044f \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0443\u0441\u044c \u043b\u0438\u0448\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u043c\u0438 \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u0441\u0442\u0432 Python \u0438 FastAPI.<\/p>\n<h4>\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h4>\n<p>\u0424\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<ul>\n<li>\n<p><strong>Jinja2<\/strong> \u2014 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 HTML;<\/p>\n<\/li>\n<li>\n<p><strong>Bootstrap<\/strong> \u2014 CSS-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p><strong>HTML, CSS \u0438 JavaScript<\/strong> \u2014 \u0434\u043b\u044f \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <strong>FastAPI<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0438 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u044b HTML.<\/p>\n<h4>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/h4>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0447\u0430\u0441\u0442\u0438:<\/p>\n<ol>\n<li>\n<p>\u0412 \u043f\u0430\u043f\u043a\u0435 <code>app<\/code> \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e <code>pages<\/code>, \u0430 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <a href=\"http:\/\/router.py\"><code>router.py<\/code><\/a> \u2014 \u0437\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>templates<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 <code>app<\/code>, \u0433\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 <code>static<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435 <code>app<\/code>, \u0433\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f css \u0438 JavaScript \u0444\u0430\u0439\u043b\u044b.<\/p>\n<\/li>\n<\/ol>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h4>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <a href=\"http:\/\/router.py\"><code>router.py<\/code><\/a> \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"python\">from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates  from app.services.auth_dep import get_current_user <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u2014 <code>Jinja2Templates<\/code>. \u041e\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d. \u042d\u0442\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0432 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445. \u0412 \u0431\u043e\u043b\u0435\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0447\u0430\u0449\u0435 \u0432\u044b\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430 React, VueJS \u0438\u043b\u0438 Angular).<\/p>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440 \u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440:<\/p>\n<pre><code class=\"python\">router = APIRouter() templates = Jinja2Templates(directory=\"app\/templates\") <\/code><\/pre>\n<h4>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0434\u043b\u044f \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0430\u0434\u0440\u0435\u0441 <code>\/<\/code>:<\/p>\n<pre><code class=\"python\">@router.get(\"\/\", response_class=HTMLResponse) async def index(request: Request, user: dict = Depends(get_current_user)):     return RedirectResponse(url=\"\/protected\") <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c <code>get_current_user<\/code>, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0449\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044f \u043d\u0430 \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 Keycloak \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430.<\/p>\n<blockquote>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0439 \u00ab\u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b\u00bb \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442. \u041e\u0434\u043d\u0430\u043a\u043e \u044d\u0442\u043e \u043d\u0435 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434. \u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043c\u0435\u0448\u0430\u0435\u0442 \u0432\u0430\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043b\u044d\u043d\u0434\u0438\u043d\u0433 \u0441 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u00ab\u0412\u043e\u0439\u0442\u0438\u00bb, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u0438\u043d\u0438\u0446\u0438\u0438\u0440\u0443\u0435\u0442 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u0432 Keycloak.<\/p>\n<\/blockquote>\n<h4>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0434\u043b\u044f \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u043e\u0441\u043b\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"python\">@router.get(\"\/protected\", response_class=HTMLResponse) async def protected_page(request: Request, user: dict = Depends(get_current_user)):     return templates.TemplateResponse(\"index.html\", {\"request\": request, \"user\": user}) <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0432 HTML-\u0448\u0430\u0431\u043b\u043e\u043d <code>index.html<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Jinja2. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u0443\u044e \u0441 \u0442\u0435\u043a\u0443\u0449\u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c.<\/p>\n<h4>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h4>\n<p>\u0421\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0441\u0442\u0430\u043d\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 HTML-\u0448\u0430\u0431\u043b\u043e\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u042d\u0442\u0438\u043c \u043c\u044b \u0437\u0430\u0439\u043c\u0451\u043c\u0441\u044f \u0434\u0430\u043b\u0435\u0435.<\/p>\n<h3>\u041e\u0431\u0437\u043e\u0440 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430: HTML + JavaScript<\/h3>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0442\u043e\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e HTML, \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Bootstrap 5.3, \u0448\u0430\u0431\u043b\u043e\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430 Jinja2 \u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0441\u0442\u0438\u043b\u0435\u0439 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432. \u0412\u0441\u0451 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u043e\u0441\u0442\u044b\u043c, \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a24\/3c0\/3b2\/a243c03b2314a0a0bcfff3b797ac3033.png\" width=\"1827\" height=\"788\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a24\/3c0\/3b2\/a243c03b2314a0a0bcfff3b797ac3033.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a24\/3c0\/3b2\/a243c03b2314a0a0bcfff3b797ac3033.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c6e\/f51\/82b\/c6ef5182b55be08720b08ebf6b8dddad.png\" width=\"1713\" height=\"796\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/c6e\/f51\/82b\/c6ef5182b55be08720b08ebf6b8dddad.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c6e\/f51\/82b\/c6ef5182b55be08720b08ebf6b8dddad.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u041e\u0431\u0449\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430<\/h4>\n<p>\u0424\u0430\u0439\u043b <code>index.html<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p><strong>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u0438\u043b\u0435\u0439 \u0438 \u0448\u0440\u0438\u0444\u0442\u043e\u0432<\/strong>:<\/p>\n<pre><code class=\"xml\">&lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.2\/dist\/css\/bootstrap.min.css\" rel=\"stylesheet\" \/&gt; &lt;link rel=\"stylesheet\" href=\"\/static\/style.css\" \/&gt; &lt;link href=\"https:\/\/fonts.googleapis.com\/css2?family=Roboto&amp;display=swap\" rel=\"stylesheet\" \/&gt; <\/code><\/pre>\n<\/li>\n<li>\n<p><strong>\u0428\u0430\u043f\u043a\u0430<\/strong> \u0441 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c \u0438 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u0432\u044b\u0445\u043e\u0434\u0430:<\/p>\n<pre><code class=\"xml\">&lt;header class=\"bg-dark text-white p-3 text-center mb-4\"&gt;   &lt;div class=\"d-flex justify-content-between align-items-center\"&gt;     &lt;h1 class=\"mb-0\"&gt;Mimo \u0417\u0430\u043c\u0435\u0442\u043a\u0438&lt;\/h1&gt;     &lt;a href=\"\/api\/logout\" class=\"btn btn-outline-light\"&gt;\u0412\u044b\u0439\u0442\u0438&lt;\/a&gt;   &lt;\/div&gt; &lt;\/header&gt; <\/code><\/pre>\n<\/li>\n<li>\n<p><strong>\u0424\u043e\u0440\u043c\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438<\/strong>:<\/p>\n<pre><code class=\"xml\">&lt;div class=\"note-input-container bg-light p-4 rounded shadow-sm mb-5\"&gt;   &lt;input type=\"text\" id=\"note-title-input\" class=\"form-control\" placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a...\" \/&gt;   &lt;textarea id=\"note-desc-input\" class=\"form-control\" rows=\"4\" placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435...\"&gt;&lt;\/textarea&gt;   &lt;button id=\"add-note-btn\" class=\"btn btn-primary float-end\"&gt;\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043a\u0443&lt;\/button&gt; &lt;\/div&gt; <\/code><\/pre>\n<\/li>\n<li>\n<p><strong>\u0421\u0435\u043a\u0446\u0438\u044f \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u0437\u0430\u043c\u0435\u0442\u043e\u043a<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 JavaScript:<\/p>\n<pre><code class=\"xml\">&lt;div id=\"notes-list\" class=\"row row-cols-1 row-cols-sm-2 row-cols-md-3 g-4\"&gt;   &lt;!-- \u0421\u044e\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 --&gt; &lt;\/div&gt; <\/code><\/pre>\n<\/li>\n<li>\n<p><strong>\u041c\u043e\u0434\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u043a\u043d\u0430<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0437\u0430\u043c\u0435\u0442\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u041a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u043d\u0438\u0445 \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u043e \u0432 \u0435\u0434\u0438\u043d\u043e\u043c \u0441\u0442\u0438\u043b\u0435 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043d\u0443\u0436\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044e (\u043a\u043b\u0438\u043a \u043f\u043e \u043a\u043d\u043e\u043f\u043a\u0435 \u0438 \u0442.\u0434.).<\/p>\n<h4>JavaScript: \u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f<\/h4>\n<p>\u0424\u0430\u0439\u043b <code>script.js<\/code>, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0439 \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u043c <code>type=\"module\"<\/code>, \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043c\u0435\u0442\u043e\u043a, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0437\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043a\u043b\u0438\u043a\u043e\u0432 \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 API:<\/p>\n<pre><code class=\"xml\">&lt;script type=\"module\" src=\"\/static\/script.js\"&gt;&lt;\/script&gt; <\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043b\u043e\u0433\u0438\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043c\u0435\u0442\u043a\u0438:<\/p>\n<pre><code class=\"javascript\">document.getElementById(\"add-note-btn\").addEventListener(\"click\", async () =&gt; {   const title = document.getElementById(\"note-title-input\").value.trim();   const description = document.getElementById(\"note-desc-input\").value.trim();    if (!title || !description) return;    await fetch(\"\/api\/notes\", {     method: \"POST\",     headers: { \"Content-Type\": \"application\/json\" },     body: JSON.stringify({ title, description })   });    \/\/ \u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043f\u043e\u043b\u0435\u0439 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 }); <\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u043c\u0435\u0442\u043e\u043a \u0447\u0435\u0440\u0435\u0437 <code>GET \/api\/notes<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u043a\u0430\u0437 \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u043a\u043e\u043d;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043c\u0435\u0442\u043a\u0438 \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c.<\/p>\n<\/li>\n<\/ul>\n<p>JavaScript \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 fetch API \u0438 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<h4>\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b<\/h4>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0430\u043f\u043a\u0443 <code>static<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 <code>app<\/code>:<\/p>\n<pre><code class=\"css\">app\/ \u251c\u2500\u2500 static\/ \u2502   \u251c\u2500\u2500 style.css       \u2190 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0441\u0442\u0438\u043b\u0438 \u2502   \u2514\u2500\u2500 script.js       \u2190 \u043b\u043e\u0433\u0438\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 <\/code><\/pre>\n<p>\u0412 <code>style.css<\/code> \u043c\u043e\u0436\u043d\u043e \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u0442\u0435\u043d\u0438, \u0444\u043e\u043d, \u043e\u0442\u0441\u0442\u0443\u043f\u044b \u0438 \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 UI-\u0434\u0435\u043a\u043e\u0440, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u043e \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u041d\u0435 \u0443\u0432\u0438\u0434\u0435\u043b \u0441\u043c\u044b\u0441\u043b\u0430 \u0432 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u043c \u0438 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u0438 JavaScript \u0438 HTML \u043a\u043e\u0434\u0430. \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0441 \u043f\u043e\u043b\u043d\u044b\u043c\u0438 \u0441\u0442\u0438\u043b\u044f\u043c\u0438, HTML, JS \u0438 \u0432\u0441\u0435\u043c Python \u043a\u043e\u0434\u043e\u043c \u0432\u044b \u043d\u0430\u0439\u0434\u0435\u0442\u0435 <a href=\"https:\/\/t.me\/PythonPathMaster\">\u0442\u0443\u0442<\/a>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0433\u043e\u0442\u043e\u0432\u044b \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b API-\u043c\u0435\u0442\u043e\u0434\u044b, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d Keycloak, \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u2014 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043b\u0438\u0448\u044c \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0451 \u0432\u043e\u0435\u0434\u0438\u043d\u043e \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. <\/p>\n<h3>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0433\u043b\u0430\u0432\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0444\u0430\u0439\u043b\u0430\u00a0<code>app\/<\/code><a href=\"http:\/\/main.py\"><code>main.py<\/code><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432, \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 Keycloak \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<h4>\u0418\u043c\u043f\u043e\u0440\u0442\u044b \u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430<\/h4>\n<p>\u0412\u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \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:<\/p>\n<pre><code class=\"python\">from contextlib import asynccontextmanager  import httpx from fastapi import FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse from fastapi.staticfiles import StaticFiles  from app.config import settings from app.services.keycloak_client import KeycloakClient  from app.api.router import router as api_router from app.pages.router import router as pages_router <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u043a \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b FastAPI, \u0442\u0430\u043a \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043a\u043b\u0438\u0435\u043d\u0442 Keycloak, \u0440\u043e\u0443\u0442\u0435\u0440\u044b API \u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446.<\/p>\n<h4>\u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u041e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0441\u0438\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u0440\u043e\u043d FastAPI \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u043c \u0446\u0438\u043a\u043b\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u044d\u0442\u0438\u043c, \u0447\u0442\u043e\u0431\u044b:<\/p>\n<ul>\n<li>\n<p>\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u00a0<code>httpx<\/code>\u00a0\u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u00a0<code>KeycloakClient<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0432\u00a0<code>app.state<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">@asynccontextmanager async def lifespan(app: FastAPI):     #\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c shared httpx \u043a\u043b\u0438\u0435\u043d\u0442     http_client = httpx.AsyncClient()     app.state.keycloak_client = KeycloakClient(http_client)      #\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440\u044b \u0438 \u0441\u0442\u0430\u0442\u0438\u043a\u0443     app.include_router(pages_router)     app.include_router(api_router)     app.mount(\"\/static\", StaticFiles(directory=\"app\/static\"), name=\"static\")      yield      #\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c httpx \u043a\u043b\u0438\u0435\u043d\u0442     await http_client.aclose()<\/code><\/pre>\n<blockquote>\n<p><em>\u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0438\u0437\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u0441 \u043e\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0435.<\/em><\/p>\n<\/blockquote>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439\u00a0<code>lifespan<\/code>:<\/p>\n<pre><code class=\"python\">app = FastAPI(lifespan=lifespan)<\/code><\/pre>\n<h4>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a: \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043f\u0440\u0438 401<\/h4>\n<p>\u041e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 401 (Unauthorized) \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 Keycloak. \u042d\u0442\u043e \u0438\u0437\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u0441 \u043e\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0440\u0443\u0447\u043a\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<pre><code class=\"python\">@app.exception_handler(HTTPException) async def auth_exception_handler(request: Request, exc: HTTPException):     if exc.status_code == 401:         return RedirectResponse(             f\"{settings.auth_url}\"             f\"?client_id={settings.CLIENT_ID}\"             f\"&amp;response_type=code\"             f\"&amp;scope=openid\"             f\"&amp;redirect_uri={settings.redirect_uri}\"         )     raise exc <\/code><\/pre>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 CORS<\/h4>\n<p>\u0414\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0435 \u0435\u0451 \u0441 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0434\u043e\u043c\u0435\u043d\u0430 \u0438\u043b\u0438 \u043f\u043e\u0440\u0442\u0430, \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c CORS (Cross-Origin Resource Sharing):<\/p>\n<pre><code class=\"python\">app.add_middleware(     CORSMiddleware,     allow_origins=[\"*\"],     allow_credentials=True,     allow_methods=[\"*\"],     allow_headers=[\"*\"], ) <\/code><\/pre>\n<blockquote>\n<p><em>\u0414\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043d\u044b\u0435 \u0434\u043e\u043c\u0435\u043d\u044b \u0432\u043c\u0435\u0441\u0442\u043e\u00a0<\/em><code><em>\"*\"<\/em><\/code><em>, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0443\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/em><\/p>\n<\/blockquote>\n<h4>\u0417\u0430\u043f\u0443\u0441\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<p>\u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435:<\/p>\n<pre><code class=\"bash\">uvicorn app.main:app --host 0.0.0.0 --port 8000 <\/code><\/pre>\n<ul>\n<li>\n<p><code>uvicorn<\/code>\u00a0\u2014 ASGI-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043a\u0430\u043a \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c;<\/p>\n<\/li>\n<li>\n<p><code>app.main:app<\/code>\u00a0\u2014 \u043f\u0443\u0442\u044c \u0434\u043e \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 FastAPI;<\/p>\n<\/li>\n<li>\n<p><code>--host 0.0.0.0<\/code>\u00a0\u2014 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u0438\u0437\u0432\u043d\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 Docker);<\/p>\n<\/li>\n<li>\n<p><code>--port 8000<\/code>\u00a0\u2014 \u043f\u043e\u0440\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d API.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/localhost:8000\">http:\/\/localhost:8000<\/a>. \u0417\u0430\u0442\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0442\u0443\u043d\u043d\u0435\u043b\u044c, \u0447\u0442\u043e\u0431\u044b \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u043b\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0438\u0437\u0432\u043d\u0435 \u0438 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 Keycloak. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Ngrok, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430.<\/p>\n<pre><code>ngrok http 8000<\/code><\/pre>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u0443\u0436\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u043a Keycloack.<\/p>\n<h4>\u0412\u0438\u0434\u0435\u043e\u043e\u0431\u0437\u043e\u0440 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u043d\u044f\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0432\u0438\u0434\u0435\u043e\u043e\u0431\u0437\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b. \u0412 \u043d\u0451\u043c \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0432\u0435\u0441\u044c \u043f\u0443\u0442\u044c: \u043e\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a API.<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/681d02d3159a09bab0653986\" data-style=\"\" id=\"681d02d3159a09bab0653986\" width=\"\"><\/div>\n<p>\u0413\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435: <a href=\"https:\/\/fastapikeycloack-yakvenalex.amvera.io\">https:\/\/fastapikeycloack-yakvenalex.amvera.io<\/a><\/p>\n<h4>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u043c, \u043e\u0441\u0442\u0430\u043b\u0441\u044f \u043b\u0438\u0448\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0430\u0433 \u2014 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0435. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043a\u0430\u0436\u0434\u043e\u043c\u0443, \u0443 \u043a\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0430, \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0447\u0430\u0442\u044c \u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f. \u0420\u0430\u043d\u0435\u0435 \u043c\u044b \u0443\u0436\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u043b\u0438 \u0432 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a> \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL \u0438 \u0441\u0435\u0440\u0432\u0438\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Keycloak. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0430\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0443\u0434\u0430 \u0438 \u043d\u0430\u0448\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u2014 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0437\u0430\u0439\u043c\u0451\u0442 \u0432\u0441\u0435\u0433\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.<\/p>\n<h3>\u0414\u0435\u043f\u043b\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043a \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u043c\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432:<\/p>\n<ol>\n<li>\n<p><strong>VPS \u0438\u043b\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440<\/strong>\u00a0\u2014 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c, \u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0430\u0432\u044b\u043a\u043e\u0432 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u043b\u0430\u0447\u043d\u044b\u0435 \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0438 \u0441 \u0430\u0432\u0442\u043e\u0434\u0435\u043f\u043b\u043e\u0435\u043c<\/strong>\u00a0\u2014 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442, \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0440\u0443\u0447\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<\/li>\n<\/ol>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f, \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u0432\u044b\u0431\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c. \u0412 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043c\u044b \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u00a0<a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\"><strong>Amvera Cloud<\/strong><\/a>\u00a0\u2014 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0435\u043c \u0430\u0432\u0442\u043e\u0434\u0435\u043f\u043b\u043e\u0439 \u0438 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<h4>\u0428\u0430\u0433\u0438 \u0434\u0435\u043f\u043b\u043e\u044f \u043d\u0430 Amvera Cloud<\/h4>\n<h3>1. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0441\u0430\u0439\u0442 Amvera \u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f\u00a0<strong>Keycloak<\/strong>.<\/p>\n<h3>2. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>\u201c\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u201d<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0438\u043f\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c\u00a0<strong>\u201c\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u201d<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0416\u043c\u0451\u043c\u00a0<strong>\u201c\u0414\u0430\u043b\u0435\u0435\u201d<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<h3>3. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<ul>\n<li>\n<p>\u041f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0442\u0430\u0440\u0438\u0444 \u043d\u0435 \u043d\u0438\u0436\u0435\u00a0<strong>\u201c\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439\u201d<\/strong>, \u0442\u0430\u043a \u043a\u0430\u043a \u0431\u043e\u043b\u0435\u0435 \u043b\u0451\u0433\u043a\u0438\u0435 \u0442\u0430\u0440\u0438\u0444\u044b \u043c\u043e\u0433\u0443\u0442 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<\/li>\n<\/ul>\n<h3>4. \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0444\u0430\u0439\u043b\u043e\u0432<\/h3>\n<p>\u041d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u044d\u0442\u0430\u043f\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>\u0427\u0435\u0440\u0435\u0437\u00a0<strong>Git<\/strong>\u00a0(Amvera \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432)<\/p>\n<\/li>\n<li>\n<p>\u0418\u043b\u0438 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0432\u044b\u0431\u0440\u0430\u0432 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 &#171;\u0427\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441&#187;<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c\u00a0<strong>\u201c\u0414\u0430\u043b\u0435\u0435\u201d<\/strong>\u00a0\u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.<\/p>\n<h3>5. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0437\u043d\u0430\u043b\u0430, \u043a\u0430\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435, \u043a\u0430\u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435 \u043d\u0438\u0436\u0435:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/aba\/297\/1de\/aba2971dea364c004ac8f4805ea2b450.png\" width=\"786\" height=\"835\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/aba\/297\/1de\/aba2971dea364c004ac8f4805ea2b450.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/aba\/297\/1de\/aba2971dea364c004ac8f4805ea2b450.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>6. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043e\u043c\u0435\u043d\u0430<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0443\u00a0<strong>\u201c\u0414\u043e\u043c\u0435\u043d\u044b\u201d<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0439 \u043f\u043e\u0434\u0434\u043e\u043c\u0435\u043d \u043e\u0442 Amvera \u0438\u043b\u0438 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/495\/eba\/8e1\/495eba8e113f33063114edb9706db85c.png\" width=\"1789\" height=\"481\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/495\/eba\/8e1\/495eba8e113f33063114edb9706db85c.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/495\/eba\/8e1\/495eba8e113f33063114edb9706db85c.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>7. \u0423\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u0434\u043e\u043c\u0435\u043d\u0430 \u0432\u00a0.env<\/h3>\n<p>\u0412 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439\u00a0<code>BASE_URL<\/code>\u00a0\u0432\u0430\u0448\u0435\u0433\u043e\u00a0<code>.env<\/code>-\u0444\u0430\u0439\u043b\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u043c\u0435\u043d. \u0412 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435:<\/p>\n<pre><code>BASE_URL=\"https:\/\/fastapikeycloack-yakvenalex.amvera.io\"<\/code><\/pre>\n<h3>8. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0432 Keycloak<\/h3>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Keycloak \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0442\u043e\u0442 \u0436\u0435\u00a0<code>BASE_URL<\/code>, \u0447\u0442\u043e \u0438 \u0432\u044b\u0448\u0435. \u042d\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/725\/6aa\/9ba\/7256aa9ba0369c41901bf9a370bdd6bc.png\" width=\"968\" height=\"671\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/725\/6aa\/9ba\/7256aa9ba0369c41901bf9a370bdd6bc.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/725\/6aa\/9ba\/7256aa9ba0369c41901bf9a370bdd6bc.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>9. \u041f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u0417\u0430\u043c\u0435\u043d\u044f\u0435\u043c .env \u0444\u0430\u0439\u043b \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0435 &#171;\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439&#187; \u043d\u0430 .env \u0444\u0430\u0439\u043b \u0441 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u0438 \u043a\u043b\u0438\u043a\u0430\u0435\u043c \u043d\u0430 &#171;\u041f\u0435\u0440\u0435\u0441\u043e\u0431\u0440\u0430\u0442\u044c&#187;.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1c4\/4b6\/732\/1c44b6732f8e5b1c20933a4810b6f9f8.png\" width=\"1773\" height=\"528\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/1c4\/4b6\/732\/1c44b6732f8e5b1c20933a4810b6f9f8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1c4\/4b6\/732\/1c44b6732f8e5b1c20933a4810b6f9f8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h4>\u0413\u043e\u0442\u043e\u0432\u043e!<\/h4>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u0434\u043e\u043c\u0435\u043d\u0443 \u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0443. \u0414\u0435\u043f\u043b\u043e\u0439 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d \u2014 \u0432\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043e\u0431\u043b\u0430\u043a\u0435.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u043c\u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c \u0432\u0430\u0441 \u0441 \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0449\u043d\u043e\u0439 \u0438 \u0433\u0438\u0431\u043a\u043e\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u043a\u0430\u043a <strong>Keycloak<\/strong>, \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0435\u0451 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u043d\u0430 <strong>FastAPI<\/strong>. \u041c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u044d\u0442\u0430\u043f\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0434\u0435\u043f\u043b\u043e\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0433\u043b\u0443\u0431\u0436\u0435 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u043d\u0435\u0435, \u0447\u0435\u043c \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0434\u043b\u044f \u0432\u0430\u0441 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0438 \u0432\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u2014 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044e UI Keycloak \u0438\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0430\u0439\u0442\u0435 \u0437\u043d\u0430\u0442\u044c. \u041e\u0446\u0435\u043d\u043a\u0430, \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0438\u043b\u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u0442\u0435\u043c\u0430 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0438 \u0441\u0442\u043e\u0438\u0442 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f.<\/p>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u2014 \u043a\u0430\u043a \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434, \u0442\u0430\u043a \u0438 \u0431\u044d\u043a\u0435\u043d\u0434 \u2014 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u043c\u043e\u0451\u043c Telegram-\u043a\u0430\u043d\u0430\u043b\u0435 <strong>\u00ab<\/strong><a href=\"https:\/\/t.me\/PythonPathMaster\"><strong>\u041b\u0451\u0433\u043a\u0438\u0439 \u043f\u0443\u0442\u044c \u0432 Python<\/strong><\/a><strong>\u00bb<\/strong>, \u0433\u0434\u0435 \u0443\u0436\u0435 \u0441\u043e\u0431\u0440\u0430\u043b\u043e\u0441\u044c \u0431\u043e\u043b\u0435\u0435 <strong>3500 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432<\/strong>. \u041c\u044b \u0434\u0435\u043b\u0438\u043c\u0441\u044f \u043e\u043f\u044b\u0442\u043e\u043c, \u043e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0438\u0434\u0435\u044f\u043c\u0438 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0430 \u0432 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0438. \u041f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u2014 \u044d\u0442\u043e \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u0438, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0444\u0435\u0441\u0441\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u043e\u0441\u0442\u0430.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u2014 \u0438 \u0434\u043e \u043d\u043e\u0432\u044b\u0445 \u0432\u0441\u0442\u0440\u0435\u0447!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/companies\/amvera\/articles\/907990\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u0421\u043b\u044b\u0448\u0430\u043b\u0438 \u043b\u0438 \u0432\u044b \u043e Keycloak \u0434\u043e \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">24.14% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430, \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445<\/span><span class=\"tm-article-poll__answer-votes\">7<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 24.14%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">44.83% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430, \u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b(\u0430)<\/span><span class=\"tm-article-poll__answer-votes\">13<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 44.83%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">31.03% <\/span><span class=\"tm-article-poll__answer-label\">\u041d\u0435\u0442, \u0432\u043f\u0435\u0440\u0432\u044b\u0435 \u0443\u0437\u043d\u0430\u043b(\u0430)<\/span><span class=\"tm-article-poll__answer-votes\">9<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 31.03%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 29 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.   \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u043b\u0441\u044f 1 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. <\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/companies\/amvera\/articles\/907990\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u0425\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0432\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u0431\u043e\u0440\u0430 Keycloak \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432\u0445\u043e\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 Google \u0438\u043b\u0438 GitHub?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">81.82% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430<\/span><span class=\"tm-article-poll__answer-votes\">18<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 81.82%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">18.18% <\/span><span class=\"tm-article-poll__answer-label\">\u041d\u0435\u0442<\/span><span class=\"tm-article-poll__answer-votes\">4<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 18.18%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 22 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.    \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u043d\u0435\u0442. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/907990\/\"> https:\/\/habr.com\/ru\/articles\/907990\/<\/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>\u0414\u0440\u0443\u0437\u044c\u044f, \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u0414\u0430\u0432\u043d\u043e \u0445\u043e\u0442\u0435\u043b \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c \u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 open-source \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u043a\u0430\u043a Keycloak \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u044f\u0437\u044b\u043a\u043e\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u043e\u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u0430\u0436\u0435 \u0432 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445.<\/p>\n<p>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u044f \u0431\u0443\u0434\u0443 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0441\u0432\u044f\u0437\u043a\u0435 Keycloak + FastAPI (Python), \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0431\u044d\u043a\u0435\u043d\u0434-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0442\u043e \u0434\u0430\u043d\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u0432\u0430\u0441 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Keycloak \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b.<\/p>\n<h3>\u041e \u0447\u0435\u043c \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f?<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u044f\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0431\u0438\u0442\u0430 \u043d\u0430 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0431\u043b\u043e\u043a\u0430:<\/p>\n<ol>\n<li>\n<p><strong>\u0422\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/strong>\u00a0\u2013 \u0437\u0434\u0435\u0441\u044c \u044f \u043a\u043e\u0440\u043e\u0442\u043a\u043e \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0432\u0430\u043c \u043f\u0440\u043e Keycloak \u0438 \u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u0445 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/strong>\u00a0\u2013 \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0431\u043e\u0439 CRUD-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0437\u0430\u043c\u0435\u0442\u043e\u043a. \u0425\u043e\u0442\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044f\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u043e\u0439, \u043d\u0430\u0448\u0430 \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u0446\u0435\u043b\u044c \u2013 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Keycloak.<\/p>\n<p>\u041a\u043b\u044e\u0447\u0435\u0432\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430: \u043f\u043e\u043f\u0430\u0434\u0430\u044f \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432\u0448\u0438\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u0430 \u0444\u043e\u0440\u043c\u0443 \u0432\u0445\u043e\u0434\u0430\/\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 Keycloak \u201c\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u201d \u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u0434 \u0441\u0432\u043e\u0438\u043c \u043b\u043e\u0433\u0438\u043d\u043e\u043c \u0438 \u043f\u0430\u0440\u043e\u043b\u0435\u043c, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043f\u0430\u0434\u0430\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043c\u0435\u0442\u043e\u043a.<\/p>\n<p>\u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c:\u00a0<strong>\u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0444\u043e\u0440\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043b\u043e\u0433\u0438\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u043f\u043e \u043f\u043e\u0447\u0442\u0435, \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043d\u0430 email \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435<\/strong>, \u0432\u0435\u0434\u044c \u044d\u0442\u043e \u0432\u0441\u0435 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 Keycloak &#8212; \u043d\u0430\u0448\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432 \u0441\u0432\u043e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0432\u0445\u043e\u0434\u0430<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\">\n<div><figcaption>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u043b\u0430\u043d \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c Keycloak<\/strong>\u00a0\u0441 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 (\u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Keycloak \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a> \u2013 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0437\u0430\u0439\u043c\u0435\u0442 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442, \u0430 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445)<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>\u00a0\u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043d\u0435 \u043f\u0443\u0442\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 Keycloak)<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c API<\/strong>\u00a0\u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0442\u043a\u0430\u043c\u0438: \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440, \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u043c\u0435\u0442\u043e\u043a \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434<\/strong>\u00a0\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Jinja2, HTML, CSS \u0438 JavaScript<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442<\/strong>\u00a0\u043d\u0430 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a> (\u0437\u0430\u0439\u043c\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0440\u0443 \u043c\u0438\u043d\u0443\u0442)<\/p>\n<\/li>\n<\/ol>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u0435\u0441\u044c \u0441 Keycloak, \u044f \u0443\u0432\u0435\u0440\u0435\u043d, \u0447\u0442\u043e \u0432\u044b \u043d\u0430\u0447\u043d\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044e \u0432 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/p>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Keycloak?<\/h3>\n<p>Keycloak \u2013 \u044d\u0442\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \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 (Identity and Access Management, IAM). \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 Keycloak \u2013 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0435\u0434\u0438\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 (Single Sign-On, SSO), \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h4>\u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438<\/h4>\n<p>Keycloak \u2013 \u044d\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0432\u0441\u044e \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e:<\/p>\n<ul>\n<li>\n<p>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044e \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f)<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0449\u0438\u0442\u0435 API \u0438 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u043d\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. Keycloak \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0442\u0438, \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432 (LDAP\/Active Directory) \u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0434\u0440\u0443\u0433\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439.<\/p>\n<h4>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 Keycloak<\/h4>\n<ul>\n<li>\n<p><strong>\u0415\u0434\u0438\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 (SSO)<\/strong>\u00a0\u2013 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432\u0445\u043e\u0434\u044f\u0442 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a\u043e \u0432\u0441\u0435\u043c \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c<\/p>\n<\/li>\n<li>\n<p><strong>\u0426\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435<\/strong>\u00a0\u2013 \u0432\u0441\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0440\u043e\u043b\u0438 \u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0435\u0434\u0438\u043d\u0443\u044e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u043f\u0430\u043d\u0435\u043b\u044c<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u043e\u0442\u043e\u0432\u044b\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/strong>\u00a0\u2013 \u0444\u043e\u0440\u043c\u044b \u0432\u0445\u043e\u0434\u0430, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u0443\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u0432<\/strong>\u00a0\u2013 \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441 OpenID Connect, OAuth 2.0 \u0438 SAML 2.0<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0435\u0430\u043b\u043c\u044b (Realms)<\/strong>\u00a0\u2013 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/strong>\u00a0\u2013 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b \u0434\u043b\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0438\u0437\u043a\u0438\u0439 \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0434\u0430<\/strong>\u00a0\u2013 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0434\u0430\u0436\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0435\u0437 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0430<\/strong>\u00a0\u2013 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0438\u0437\u0430\u0439\u043d \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u0434 \u0432\u0430\u0448 \u0431\u0440\u0435\u043d\u0434 (\u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u043c)<\/p>\n<\/li>\n<\/ul>\n<h4>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Keycloak<\/h4>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 Keycloak \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u0435\u0440\u0432\u0435\u0440 Keycloak<\/strong>\u00a0\u2013 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0434\u0430\u043f\u0442\u0435\u0440\u044b Keycloak<\/strong>\u00a0\u2013 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0438\u043b\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u0432\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c Keycloak (\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0440\u0435\u0447\u044c \u043f\u0440\u043e FastAPI)<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<ol>\n<li>\n<p><strong>\u0417\u0430\u043f\u0440\u043e\u0441 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u0430<\/strong>\u00a0\u2014 \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u0441\u0443\u0440\u0441\u0443 \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 Keycloak<\/strong>\u00a0\u2014 \u0410\u0434\u0430\u043f\u0442\u0435\u0440 Keycloak \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d, \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 Keycloak \u0434\u043b\u044f \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0432\u043e\u0434 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong>\u00a0\u2014 \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0445\u043e\u0434\u0430 Keycloak, \u0433\u0434\u0435 \u0432\u0432\u043e\u0434\u0438\u0442 \u0441\u0432\u043e\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043b\u043e\u0433\u0438\u043d\/\u043f\u0430\u0440\u043e\u043b\u044c) \u0438\u043b\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 Google \u0438\u043b\u0438 Facebook) \/ \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430<\/strong>\u00a0\u2014 \u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 Keycloak \u043d\u0435 \u0441\u0440\u0430\u0437\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u043e\u043a\u0435\u043d\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u044d\u0442\u0438\u043c \u043a\u043e\u0434\u043e\u043c.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u043c\u0435\u043d \u043a\u043e\u0434\u0430 \u043d\u0430 \u0442\u043e\u043a\u0435\u043d\u044b<\/strong>\u00a0\u2014 \u0412\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043a Keycloak \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u0430 \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043d\u0430 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u042d\u0442\u043e\u0442 \u0448\u0430\u0433 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u201c\u0437\u0430 \u043a\u0443\u043b\u0438\u0441\u0430\u043c\u0438\u201d \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u043c\u0438, \u0431\u0435\u0437 \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/strong>\u00a0\u2014 \u0412 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 Keycloak \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u0442\u0440\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0442\u043e\u043a\u0435\u043d\u0430:<\/p>\n<ul>\n<li>\n<p><strong>access_token<\/strong>\u00a0\u2014 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c (\u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, 5-30 \u043c\u0438\u043d\u0443\u0442)<\/p>\n<\/li>\n<li>\n<p><strong>refresh_token<\/strong>\u00a0\u2014 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e access_token \u0431\u0435\u0437 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \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\u044f (\u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043e\u043b\u044c\u0448\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439)<\/p>\n<\/li>\n<li>\n<p><strong>id_token<\/strong>\u00a0\u2014 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 (\u043f\u0440\u043e\u0444\u0438\u043b\u044c)<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c<\/strong>\u00a0\u2014 \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 access_token \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, Keycloak \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c\u0438, \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<h3>\u041a\u043e\u0433\u0434\u0430 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Keycloak?<\/h3>\n<p>Keycloak \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f\u0445:<\/p>\n<ol>\n<li>\n<p><strong>\u0412\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0449\u0443\u044e \u0431\u0430\u0437\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. Keycloak \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0430\u043c \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442 \u0435\u0434\u0438\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0432\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/strong>\u00a0&#8212; \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0432\u0445\u043e\u0434 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0442\u0438, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f, \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f email \u0438 \u0442.\u0434.<\/p>\n<\/li>\n<li>\n<p><strong>\u0423 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e. Keycloak \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0430\u043c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438<\/strong>\u00a0&#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u043e\u043b\u0435\u0439 \u0438 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0447\u0435\u0440\u0435\u0437 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0435 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/strong>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (LDAP, Active Directory).<\/p>\n<\/li>\n<\/ol>\n<h3>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b Keycloak<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043c\u044b \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p><strong>Realm (\u0420\u0435\u0430\u043b\u043c)<\/strong>\u00a0\u2014 \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u0440\u043e\u043b\u0438 \u0438 \u0433\u0440\u0443\u043f\u043f\u044b. \u041c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0430\u043b\u043c\u043e\u0432 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438\u043b\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><strong>Client (\u041a\u043b\u0438\u0435\u043d\u0442)<\/strong>\u00a0\u2014 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0443 Keycloak. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0448\u0435 FastAPI-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>User (\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c)<\/strong>\u00a0\u2014 \u0441\u0443\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 Keycloak.<\/p>\n<\/li>\n<li>\n<p><strong>Role (\u0420\u043e\u043b\u044c)<\/strong>\u00a0\u2014 \u043d\u0430\u0431\u043e\u0440 \u043f\u0440\u0430\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c. \u0420\u043e\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u044b \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0438\u043b\u0438 \u0431\u044b\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u043c\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>Group (\u0413\u0440\u0443\u043f\u043f\u0430)<\/strong>\u00a0\u2014 \u043d\u0430\u0431\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0440\u043e\u043b\u0438.<\/p>\n<\/li>\n<li>\n<p><strong>Protocol (\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b)<\/strong>\u00a0\u2014 Keycloak \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u0432 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f OpenID Connect (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 OAuth 2.0) \u0438 SAML 2.0. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c OpenID Connect.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 Keycloak \u0438 \u0435\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0430.<\/p>\n<h3>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c Keycloak \u0441 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043b\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u043c\u043e\u0438\u043c \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u0442\u0432\u043e\u043c, \u0442\u043e \u0437\u043d\u0430\u0435\u0442\u0435, \u0447\u0442\u043e \u044f, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u0443\u044e\u0441\u044c \u0432 \u0441\u0442\u0430\u0442\u044c\u044f\u0445 \u043d\u0430 \u043d\u043e\u0432\u0438\u0447\u043a\u043e\u0432. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u0434\u0430\u043b\u0435\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0441\u0430\u043c\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u0435 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0442\u0435\u043b\u043e\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0412 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0438, \u043a\u0430\u043a \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0434 Keycloak, \u0442\u0430\u043a \u0438 \u0441\u0430\u043c\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b Keycloak \u2013 \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera Cloud<\/a>.<\/p>\n<h4>\u0412\u0430\u0436\u043d\u043e \u043e \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/h4>\n<p>\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0430. \u0422\u043e \u0435\u0441\u0442\u044c, \u043d\u0435 \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0438 \u0434\u043b\u044f Keycloak. \u0414\u043b\u044f Keycloak \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 MySQL \u0438 PostgreSQL. \u041c\u044b \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c PostgreSQL.<\/p>\n<p>\u041f\u043e\u0434\u043d\u044f\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u043a\u0430\u043a \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430 VPS \u0441\u0435\u0440\u0432\u0435\u0440\u0435), \u043b\u0438\u0431\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 Amvera. \u0412\u044b\u0431\u0435\u0440\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u0440\u043e\u0449\u0435 \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0430\u0432\u044b\u043a\u043e\u0432.<\/p>\n<h4>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c PostgreSQL \u043d\u0430 Amvera Cloud<\/h4>\n<ol>\n<li>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 <a href=\"https:\/\/amvera.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=yakvenalex_keycloack_fastapi\">Amvera<\/a>, \u0435\u0441\u043b\u0438 \u0435\u0449\u0451 \u043d\u0435 \u0431\u044b\u043b\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u0438 \u0432\u0445\u043e\u0434\u0438\u043c<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u00abPostgreSQL\u00bb<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445\u00bb<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c \u00ab\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u00bb, \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0442\u0430\u0440\u0438\u0444 (\u043d\u0435 \u043d\u0438\u0436\u0435 \u00ab\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439\u00bb)<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0421\u0423\u0411\u0414 PostgreSQL:<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\u00bb \u0438 \u0434\u043e\u0436\u0438\u0434\u0430\u0435\u043c\u0441\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u0441\u0442\u0430\u0442\u0443\u0441 \u00abPostgreSQL \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u00bb.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"\"><\/figure>\n<p>\u042d\u0442\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f Keycloak. \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 &#8212; \u043e\u043d\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u044d\u0442\u0430\u043f\u0435.<\/p>\n<h4>\u041f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c \u0441\u0430\u043c Keycloak<\/h4>\n<p>\u0414\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f Keycloak \u0432\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0448\u0430\u0433\u0438:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0422\u0438\u043f: \u041f\u0440\u0435\u0434\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0437 \u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\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-459209","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/459209","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=459209"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/459209\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=459209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=459209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=459209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}