{"id":392753,"date":"2024-06-29T10:35:46","date_gmt":"2024-06-29T10:35:46","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=392753"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=392753","title":{"rendered":"<span>Token-Based Authetification \u0432 \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0445 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c Qt6 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Qr-\u043a\u043e\u0434\u043e\u0432. Http-\u0441\u0435\u0440\u0432\u0435\u0440<\/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<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/733788\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 1. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/733832\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 2. \u0421\u0435\u0440\u0432\u0435\u0440<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/734032\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 3. \u0410\u0434\u043c\u0438\u043d\u0441\u043a\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<\/ul>\n<h2>\u041e\u0431\u0449\u0430\u044f \u0441\u0445\u0435\u043c\u0430<\/h2>\n<p>\u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 (\u0441\u0430\u043c\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438).<\/p>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0441 localhost \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f http-\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a Authorization \u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0438 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u0442\u043e\u043a\u0435\u043d\u0430.<\/p>\n<p>\u0414\u043b\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0431\u0443\u0434\u0443\u0442 \u0434\u0432\u0435 \u0440\u0443\u0447\u043a\u0438: \u043e\u0434\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 json-\u043c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0438\u043c\u0435\u043d\u0430\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a, \u0432\u0442\u043e\u0440\u0430\u044f \u2014 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043f\u043e \u0438\u043c\u0435\u043d\u0438.<\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 crud, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e localhost.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a64\/e03\/189\/a64e0318956eb502dec701da5d02dfdd.png\" width=\"400\" height=\"474\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a64\/e03\/189\/a64e0318956eb502dec701da5d02dfdd.png\"\/><\/figure>\n<h3>\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e\u0439 read-only \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043d\u0430 \u0434\u0432\u0430 \u043c\u0435\u0442\u043e\u0434\u0430: \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u043c\u0451\u043d, \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043f\u043e \u0438\u043c\u0435\u043d\u0438. \u041d\u0438\u0447\u0435\u0433\u043e \u043b\u0438\u0448\u043d\u0435\u0433\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ImageRepository<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>ImageRepository.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { class ImageRepository { private:     QDir m_root; public:     ImageRepository(const QString &amp;root = QDir::rootPath()); public:     QImage image(const QString&amp; name);     QStringList images() const; }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>ImageRepository.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { ImageRepository::ImageRepository(const QString &amp;root)     :m_root{ root } { m_root.setNameFilters(QStringList{} &lt;&lt; \"*.png\" &lt;&lt; \"*.jpg\" &lt;&lt; \"*.jpeg\"); }  QImage ImageRepository::image(const QString &amp;name) {     return QImage{ m_root.filePath(name) }; } QStringList ImageRepository::images() const {     return m_root.entryList(QDir::Filter::Files | QDir::Filter::Readable); } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/details>\n<h3>\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/h3>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0451 \u043c\u0430\u043b\u043e\u0441\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435: \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0447\u0442\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0438\u0441\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c (existence \u0438 expiration). \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448 \u0432\u0435\u043b\u0438\u043a\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u043e\u0436\u0435\u0442 \u0432 \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443\u043f\u0430\u0441\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c QUuid, \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c \u0432 \u0432\u0438\u0434\u0435 QDateTime, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u2014 \u0441\u0442\u0430\u0440\u044b\u0439 \u0434\u043e\u0431\u0440\u044b\u0439 QSettings. \u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043a\u043b\u044e\u0447-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0433\u0434\u0435 uuid \u2014 \u043a\u043b\u044e\u0447, \u0430 expiration \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c 4 \u043c\u0435\u0442\u043e\u0434\u0430:<\/p>\n<ul>\n<li>\n<p>createToken \u2014 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 <\/p>\n<\/li>\n<li>\n<p>removeToken \u2014 \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u043f\u0440\u0438 \u0435\u0433\u043e \u043d\u0430\u043b\u0438\u0447\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>isValidToken \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 existence \u0438 expiration \u0443 \u0442\u043e\u043a\u0435\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>tokens \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0430\u043f\u0443 token-expiration<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434 <code>removeExpiredTokens()<\/code>, \u0443\u0434\u0430\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0441\u0435 \u043f\u0440\u043e\u0442\u0443\u0445\u0448\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f TokenRepository<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>TokenRepository.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { class TokenRepository { private:     static inline constexpr const char* s_tokens = \"tokens\";     QSettings m_storage; public:     TokenRepository(const QString&amp; path = {}); public:     bool isValidToken(const QUuid&amp; bearer);     QMap&lt;QUuid, QDateTime> tokens(); public:     QUuid createToken(const QDateTime&amp; expiration);     void removeToken(const QUuid&amp; bearer); private:     void removeExpiredTokens(); }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenRepository.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { TokenRepository::TokenRepository(const QString &amp;path)     :m_storage{ path, QSettings::Format::IniFormat } {          m_storage.beginGroup(s_tokens);      }   bool TokenRepository::isValidToken(const QUuid &amp;token) {     removeExpiredTokens();     return m_storage.contains(token.toString()); } QMap&lt;QUuid, QDateTime> TokenRepository::tokens() {     removeExpiredTokens();     QMap&lt;QUuid, QDateTime> result{};     for(const auto&amp; key: m_storage.allKeys())         result[QUuid::fromString(key)] = m_storage.value(key).toDateTime();      return result; }  QUuid TokenRepository::createToken(const QDateTime&amp; expiration) {     removeExpiredTokens();     const auto token = QUuid::createUuid();     m_storage.setValue(token.toString(), expiration);     return token; } void TokenRepository::removeToken(const QUuid &amp;token) {     removeExpiredTokens();     m_storage.remove(token.toString()); }  void TokenRepository::removeExpiredTokens() {     const auto current = QDateTime::currentDateTime();     for(const auto&amp; key: m_storage.allKeys())         if(current > m_storage.value(key).toDateTime())             m_storage.remove(key); } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u043e CoreGuidelines \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b <code>isValidToken<\/code> \u0438 <code>tokens<\/code> \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u043d\u044b\u043c\u0438, \u0430 m_storage \u0441\u0434\u0435\u043b\u0430\u0442\u044c mutable, \u0434\u0430\u0431\u044b \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044c \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0438 \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u044c \u0435\u0451 \u043e\u0442 \u0431\u0438\u043d\u0430\u0440\u043d\u043e\u0439, \u043d\u043e \u0442\u0443\u0442 \u044f \u0440\u0435\u0448\u0438\u043b \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<\/div>\n<\/details>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b<\/h3>\n<p>\u042d\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0443\u0436\u043d\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 json \u0432 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438. \u041d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e.<\/p>\n<p>\u0422\u0443\u0442 \u043c\u044b \u0443\u0436\u0435 \u0443\u0432\u0438\u0434\u0438\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 QHttpServerResponse. \u042d\u0442\u043e \u043a\u043b\u0430\u0441\u0441, \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b\u0439 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0431\u0430\u0439\u0442\u043e\u0432, \u0441\u0442\u0440\u043e\u043a\u0443, json, \u043a\u043e\u0440\u043e\u0447\u0435 \u0432\u0441\u0451, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u0438\u0439 http-\u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ImageController<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>ImageController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { class ImageController { private:     std::shared_ptr&lt;storages::ImageRepository> m_images; public:     ImageController(const std::shared_ptr&lt;storages::ImageRepository> &amp;images); public:     QHttpServerResponse image(const QString&amp; name) const;     QHttpServerResponse imagesList() const; }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>ImageController.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { ImageController::ImageController(const std::shared_ptr&lt;storages::ImageRepository> &amp;images)     :m_images{ std::move(images) } {}  QHttpServerResponse ImageController::image(const QString &amp;name) const {     QByteArray result{};     \/\/QBuffer - \u043f\u0440\u043e\u0441\u0442\u043e\u0439 QIODevice \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 QByteArray     QBuffer buffer{ &amp;result };     m_images->image(name).save(&amp;buffer, \"PNG\");     return QHttpServerResponse{ result }; } QHttpServerResponse ImageController::imagesList() const {     return QHttpServerResponse{ QJsonArray::fromStringList(m_images->images()) }; } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f TokenController<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>TokenController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { class TokenController { private:     std::shared_ptr&lt;storages::TokenRepository> m_tokens; public:     TokenController(const std::shared_ptr&lt;storages::TokenRepository> &amp;tokens); public:     QHttpServerResponse createToken(quint64 expirationSpan);     QHttpServerResponse removeToken(const QByteArray&amp; token);     QHttpServerResponse getAllTokens(); }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenController.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { TokenController::TokenController(const std::shared_ptr&lt;storages::TokenRepository> &amp;tokens)     :m_tokens{ std::move(tokens) } { }  QHttpServerResponse TokenController::createToken(quint64 expirationSpan) {     return QHttpServerResponse{ m_tokens->createToken(QDateTime::currentDateTime().addSecs(expirationSpan)).toString() }; } QHttpServerResponse TokenController::removeToken(const QByteArray &amp;bearer) {     m_tokens->removeToken(QUuid::fromString(bearer));     return QHttpServerResponse{ QHttpServerResponse::StatusCode::Accepted }; } QHttpServerResponse TokenController::getAllTokens() {     QJsonObject result{};     const auto elements = m_tokens->tokens();     for(auto iter = elements.begin(); iter != elements.end(); ++iter)         result[iter.key().toString(QUuid::StringFormat::WithoutBraces)] = iter.value().toSecsSinceEpoch();      return result; } }<\/code><\/pre>\n<p>\u0414<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b json-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0432 \u043c\u0435\u0442\u043e\u0434 createToken \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u043e\u043a\u0435\u043d \u0431\u0443\u0434\u0435\u0442 \u0436\u0438\u0442\u044c: \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043a\u0435\u043d, \u0436\u0438\u0432\u0443\u0449\u0438\u0439 \u0441\u0443\u0442\u043a\u0438, \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c 24 * 60 * 60 = 86400.<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e, \u044d\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0438\u0447\u0435\u0433\u043e, \u043a\u0440\u043e\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 json-\u043e\u0432.   <\/p>\n<h3>Http-\u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f<\/h3>\n<p>\u041d\u0443 \u0438, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u0441\u0430\u043c\u0430\u044f \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<p>\u041c\u044b \u0440\u0435\u0448\u0438\u043b\u0438, \u0447\u0442\u043e \u0434\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043b\u044e\u0431\u043e\u043c\u0443, \u0443 \u043a\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443, \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b, \u044d\u0442\u043e\u0442 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0431\u0443\u0434\u0435\u0442 \u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u043e \u043d\u0430 \u0432\u044b\u0434\u0430\u0447\u0443 \u0442\u043e\u043a\u0435\u043d\u043e\u0432.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0444\u0438\u043b\u044c\u0442\u0440. \u0414\u043b\u044f \u043d\u0435\u0433\u043e \u0432\u0432\u0435\u0434\u0451\u043c \u0434\u0432\u0430 \u043a\u043b\u0430\u0441\u0441\u0430:<\/p>\n<ul>\n<li>\n<p>AbstractHttpController \u2014 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 N \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 QHttpServerResponse.<\/p>\n<\/li>\n<li>\n<p>TokenAuthorizator \u2014 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0444\u0438\u043b\u044c\u0442\u0440, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 N \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 QHttpServerRequest \u043d\u0430 \u043a\u043e\u043d\u0446\u0435.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>AbstractHttpController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace utils { template&lt;typename...Args> struct AbstractHttpController {     Q_DISABLE_COPY(AbstractHttpController);     virtual QHttpServerResponse handle(const Args&amp;...) = 0;     virtual ~AbstractHttpController() = default;     AbstractHttpController() = default; }; }<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c \u0438 \u0434\u0435\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c \u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u043c handle, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u043c variadic template.<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenAuthorizator.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace utils { template&lt;typename...Args> class TokenAuthorizator: public AbstractHttpController&lt;Args..., QHttpServerRequest> { private:     std::function&lt;QHttpServerResponse(const Args&amp;...)> m_next;     std::shared_ptr&lt;storages::TokenRepository> m_tokens; public:     TokenAuthorizator(std::shared_ptr&lt;storages::TokenRepository> tokens, const std::function&lt;QHttpServerResponse(const Args&amp;...)>&amp; next)         :m_next{ std::move(next) }, m_tokens{ tokens } {} public:     virtual QHttpServerResponse handle(const Args&amp;...parametes, const QHttpServerRequest&amp; request) override {         if(not request.remoteAddress().isLoopback())             if(not m_tokens->isValidToken(QUuid::fromString(request.value(\"Authorization\"))))                 return QHttpServerResponse::StatusCode::Unauthorized;          return m_next(parametes...);     } }; }<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0443\u0436\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u041a\u043b\u0430\u0441\u0441 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 Args&#8230; \u0438 QHttpServerRequest. Args&#8230; \u043e\u043d \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0434\u0430\u043b\u044c\u0448\u0435, \u0430 \u043f\u043e QHttpServerRequest \u0434\u0435\u043b\u0430\u0435\u0442 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044e. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043d\u0443\u0436\u043d\u0430 \u0435\u0449\u0451 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 QHttpServerRequest \u0442\u043e\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 handle \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0434\u0451\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0440\u0438\u0448\u0451\u043b \u043d\u0435 \u0441 <strong>::1<\/strong> (\u0441 \u044d\u0442\u043e\u0433\u043e \u041f\u041a), \u0430 \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0438\u0437\u0432\u043d\u0435. \u0418 \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u0435\u0434\u0448\u0438\u0439 \u0438\u0437\u0432\u043d\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f code 401 (Unauthorized).<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c tokens \u0432 TokenAuthorizator \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0421\u0442\u043e\u0438\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u043a\u043b\u0430\u0434\u043a\u0443 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442\u0430, \u0430 \u0443\u0436\u0435 tokens \u0437\u0430\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c \u0432 \u044d\u0442\u043e\u0442 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442. \u042d\u0442\u043e \u0443\u0434\u0430\u043b\u0438\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u044d\u0442\u0438\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438.<\/p>\n<\/li>\n<\/ol>\n<\/div>\n<\/details>\n<h2>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0451 \u0432 \u043a\u0443\u0447\u0443<\/h2>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043b\u0438\u0448\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u0442\u0438 \u043a\u0443\u0441\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u0435. \u0423\u0434\u043e\u0431\u043d\u043e\u0433\u043e Dependency Injection, \u043a\u0430\u043a \u0432 C#, \u043c\u044b \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u043d\u0435 \u0438\u043c\u0435\u0435\u043c, \u0434\u0430 \u0438 \u0442\u0443\u0442 \u043e\u043d \u043f\u043e \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0438\u0437\u043b\u0438\u0448\u0435\u043d. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u043f\u0440\u044f\u043c\u043e \u0432 main. <\/p>\n<details class=\"spoiler\">\n<summary>main.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">QCoreApplication app{ argc, argv };     if(app.arguments().size() == 2)         qFatal(\"Use app: &lt;app-name> &lt;image-dir> &lt;tokens-storage>\");  auto images = std::make_shared&lt;ImageRepository>(argv[1]); auto tokens = std::make_shared&lt;TokenRepository>(argv[2]); auto server = std::make_shared&lt;QHttpServer>();  auto imageController = std::make_shared&lt;ImageController>(images); auto tokenController = std::make_shared&lt;TokenController>(tokens);  \/\/\u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f std::bind \u0438\u043b\u0438 std::bind_from (since C++20) auto getAllTokens = std::make_shared&lt;TokenAuthorizator&lt;>>(tokens,     [tokenController]() { return tokenController->getAllTokens(); }); auto createToken = std::make_shared&lt;TokenAuthorizator&lt;quint64>>(tokens,     [tokenController](quint64 expiration) { return tokenController->createToken(expiration); }); auto removeToken = std::make_shared&lt;TokenAuthorizator&lt;QByteArray>>(tokens,     [tokenController](const QByteArray&amp; token) { return tokenController->removeToken(token); });  auto getImagesList = std::make_shared&lt;TokenAuthorizator&lt;>>(tokens,     [imageController]() { return imageController->imagesList(); }); auto getImage = std::make_shared&lt;TokenAuthorizator&lt;QString>>(tokens,     [imageController](const QString&amp; image) { return imageController->image(image); });  \/\/\u041f\u0440\u043e api \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043a\u0430\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c route \u043c\u043e\u0436\u043d\u043e \/\/\u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0442\u0443\u0442: https:\/\/doc.qt.io\/qt-6\/qhttpserver.html server->route(\"\/auth\/token\/all\/\", [getAllTokens](const QHttpServerRequest&amp; request) {     return getAllTokens->handle(request); }); server->route(\"\/auth\/token\/create\/&lt;arg>\", [createToken](quint64 expirationSpan, const QHttpServerRequest&amp; request) {     return createToken->handle(expirationSpan, request); }); server->route(\"\/auth\/token\/remove\/&lt;arg>\", [removeToken](const QByteArray&amp; token, const QHttpServerRequest&amp; request) {     return removeToken->handle(token, request); });  server->route(\"\/data\/images\/list\", [getImagesList](const QHttpServerRequest&amp; request) {     return getImagesList->handle(request); }); server->route(\"\/data\/images\/&lt;arg>\", [getImage](const QString&amp; image, const QHttpServerRequest&amp; request) {     return getImage->handle(image, request); });  \/\/\u041e\u0442\u0432\u0435\u0447\u0430\u0435\u043c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441 \u043b\u044e\u0431\u044b\u0445 \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u0440\u0442 5555 server->listen(QHostAddress::SpecialAddress::Any, 5555); return app.exec();<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c<\/h2>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u0432\u0443\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432: \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440 (\u043d\u043e\u0443\u0442\u0431\u0443\u043a), \u0441 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b (\u043a\u0430\u0447\u0430\u0435\u043c \u043b\u044e\u0431\u043e\u0439 API-tester \u043d\u0430 \u043c\u043e\u0431\u0438\u043b\u0443 \u0438 \u0440\u0430\u0434\u0443\u0435\u043c\u0441\u044f \u0436\u0438\u0437\u043d\u0438).<\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u043d\u043e\u0443\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u0431\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0432\u0441\u0451 \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u0422\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u2014 \u043f\u043b\u043e\u0445\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432 \u043d\u043e\u043c\u0435\u0440\u0430\u0445 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0431\u0435\u043b\u044b. \u041d\u043e \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u0441\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0432\u044b, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0431\u0443\u0434\u0435\u0442\u0435 \u043a\u0430\u043a-\u0442\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0442\u044c \u0438\u043c \u0438\u043c\u0435\u043d\u0430 \u0438 \u0442.\u043f.<\/p>\n<details class=\"spoiler\">\n<summary>\u0422\u0435\u0441\u0442 \u043d\u0430 \u043d\u043e\u0443\u0442\u0435<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/908\/90a\/e6b\/90890ae6b742f2c4de054cbbcb63f3d1.png\" alt=\"\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\" title=\"\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\" width=\"617\" height=\"155\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/908\/90a\/e6b\/90890ae6b742f2c4de054cbbcb63f3d1.png\"\/><\/p>\n<div><figcaption>\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f56\/c14\/3c2\/f56c143c221f0876193174c53292f162.png\" alt=\"\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\" title=\"\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\" width=\"1221\" height=\"828\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f56\/c14\/3c2\/f56c143c221f0876193174c53292f162.png\"\/><\/p>\n<div><figcaption>\u0417\u0430\u043f\u0440\u043e\u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<\/div>\n<\/details>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043d\u043e\u0443\u0442\u0431\u0443\u043a \u0438 \u0442\u0435\u043b\u0435\u0444\u043e\u043d, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0440\u0430\u0437\u0434\u0430\u0442\u044c Wifi \u0441 \u043e\u0434\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0440\u0443\u0433\u043e\u0433\u043e. <\/p>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f3f\/960\/fdf\/f3f960fdfc5d046a110947c58bee2bb6.png\" width=\"1154\" height=\"1280\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f3f\/960\/fdf\/f3f960fdfc5d046a110947c58bee2bb6.png\"\/><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u043d\u0435 \u0432\u044b\u0445\u043e\u0434\u0438\u0442. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043f\u043e \u0442\u043e\u043a\u0435\u043d\u0443:<\/p>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0441 \u0434\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u043d\u043e\u0443\u0442\u0431\u0443\u043a\u0430) \u0437\u0430\u043f\u0440\u043e\u0441 <strong>http:\/\/127.0.0.1:5555\/auth\/token\/create\/86400<\/strong><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c9f\/5e8\/290\/c9f5e8290da07a937806d65affd55779.png\" alt=\"\u0412 \u043e\u0442\u0432\u0435\u0442\u0435 \u043f\u0440\u0438\u0434\u0451\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d\" title=\"\u0412 \u043e\u0442\u0432\u0435\u0442\u0435 \u043f\u0440\u0438\u0434\u0451\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d\" width=\"512\" height=\"125\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c9f\/5e8\/290\/c9f5e8290da07a937806d65affd55779.png\"\/><\/p>\n<div><figcaption>\u0412 \u043e\u0442\u0432\u0435\u0442\u0435 \u043f\u0440\u0438\u0434\u0451\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d<\/figcaption><\/div>\n<\/figure>\n<p>\u0412\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u0432 \u0445\u0435\u0434\u0435\u0440 Authorization, \u0438 \u0432\u0441\u0451 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1a1\/171\/fad\/1a1171fad4247175c0883a554280b76b.png\" alt=\"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\" title=\"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\" width=\"1154\" height=\"1280\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a1\/171\/fad\/1a1171fad4247175c0883a554280b76b.png\"\/><\/p>\n<div><figcaption>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f<\/figcaption><\/div>\n<\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u044b, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0431\u0443\u0434\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0442\u0438\u043f\u0430 OAuth. \u0414\u043b\u044f OAuth \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0438\u0435: \u043f\u0435\u0440\u0435\u0434 \u043a\u043e\u0434\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u043e\u0432\u043e Bearer.<\/p>\n<p>\u0422.\u0435. <br \/><strong>Authorzation: 0d2c7f09-8a3a-4750-8d47-9a052bb1587f<\/strong> <br \/>\u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u0441\u044f \u0432 <br \/><strong>Authorzation: Bearer 0d2c7f09-8a3a-4750-8d47-9a052bb1587f<\/strong><\/p>\n<p>\u041d\u043e \u043e\u0441\u043e\u0431\u043e\u0439 \u0440\u0430\u0437\u043d\u0438\u0446\u044b \u0432 \u044d\u0442\u043e\u043c \u043d\u0435\u0442.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/733832\/\"> https:\/\/habr.com\/ru\/articles\/733832\/<\/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<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/733788\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 1. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/733832\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 2. \u0421\u0435\u0440\u0432\u0435\u0440<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/734032\/\" rel=\"noopener noreferrer nofollow\">\u0427\u0430\u0441\u0442\u044c 3. \u0410\u0434\u043c\u0438\u043d\u0441\u043a\u0438\u0439 \u043a\u043b\u0438\u0435\u043d\u0442<\/a><\/p>\n<\/li>\n<\/ul>\n<h2>\u041e\u0431\u0449\u0430\u044f \u0441\u0445\u0435\u043c\u0430<\/h2>\n<p>\u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 (\u0441\u0430\u043c\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438).<\/p>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0441 localhost \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f http-\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a Authorization \u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0438 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u0442\u043e\u043a\u0435\u043d\u0430.<\/p>\n<p>\u0414\u043b\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0431\u0443\u0434\u0443\u0442 \u0434\u0432\u0435 \u0440\u0443\u0447\u043a\u0438: \u043e\u0434\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 json-\u043c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0438\u043c\u0435\u043d\u0430\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a, \u0432\u0442\u043e\u0440\u0430\u044f \u2014 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043f\u043e \u0438\u043c\u0435\u043d\u0438.<\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 crud, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e localhost.<\/p>\n<figure class=\"\"><\/figure>\n<h3>\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e\u0439 read-only \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043d\u0430 \u0434\u0432\u0430 \u043c\u0435\u0442\u043e\u0434\u0430: \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u043c\u0451\u043d, \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043f\u043e \u0438\u043c\u0435\u043d\u0438. \u041d\u0438\u0447\u0435\u0433\u043e \u043b\u0438\u0448\u043d\u0435\u0433\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ImageRepository<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>ImageRepository.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { class ImageRepository { private:     QDir m_root; public:     ImageRepository(const QString &amp;root = QDir::rootPath()); public:     QImage image(const QString&amp; name);     QStringList images() const; }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>ImageRepository.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { ImageRepository::ImageRepository(const QString &amp;root)     :m_root{ root } { m_root.setNameFilters(QStringList{} &lt;&lt; \"*.png\" &lt;&lt; \"*.jpg\" &lt;&lt; \"*.jpeg\"); }  QImage ImageRepository::image(const QString &amp;name) {     return QImage{ m_root.filePath(name) }; } QStringList ImageRepository::images() const {     return m_root.entryList(QDir::Filter::Files | QDir::Filter::Readable); } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/details>\n<h3>\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/h3>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0451 \u043c\u0430\u043b\u043e\u0441\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435: \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0447\u0442\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0438\u0441\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c (existence \u0438 expiration). \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448 \u0432\u0435\u043b\u0438\u043a\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u043e\u0436\u0435\u0442 \u0432 \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443\u043f\u0430\u0441\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c QUuid, \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c \u0432 \u0432\u0438\u0434\u0435 QDateTime, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u2014 \u0441\u0442\u0430\u0440\u044b\u0439 \u0434\u043e\u0431\u0440\u044b\u0439 QSettings. \u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043a\u043b\u044e\u0447-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0433\u0434\u0435 uuid \u2014 \u043a\u043b\u044e\u0447, \u0430 expiration \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c 4 \u043c\u0435\u0442\u043e\u0434\u0430:<\/p>\n<ul>\n<li>\n<p>createToken \u2014 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 <\/p>\n<\/li>\n<li>\n<p>removeToken \u2014 \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u043f\u0440\u0438 \u0435\u0433\u043e \u043d\u0430\u043b\u0438\u0447\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>isValidToken \u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 existence \u0438 expiration \u0443 \u0442\u043e\u043a\u0435\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>tokens \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0430\u043f\u0443 token-expiration<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u043c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434 <code>removeExpiredTokens()<\/code>, \u0443\u0434\u0430\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0441\u0435 \u043f\u0440\u043e\u0442\u0443\u0445\u0448\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u044b.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f TokenRepository<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>TokenRepository.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { class TokenRepository { private:     static inline constexpr const char* s_tokens = \"tokens\";     QSettings m_storage; public:     TokenRepository(const QString&amp; path = {}); public:     bool isValidToken(const QUuid&amp; bearer);     QMap&lt;QUuid, QDateTime> tokens(); public:     QUuid createToken(const QDateTime&amp; expiration);     void removeToken(const QUuid&amp; bearer); private:     void removeExpiredTokens(); }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenRepository.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace storages { TokenRepository::TokenRepository(const QString &amp;path)     :m_storage{ path, QSettings::Format::IniFormat } {          m_storage.beginGroup(s_tokens);      }   bool TokenRepository::isValidToken(const QUuid &amp;token) {     removeExpiredTokens();     return m_storage.contains(token.toString()); } QMap&lt;QUuid, QDateTime> TokenRepository::tokens() {     removeExpiredTokens();     QMap&lt;QUuid, QDateTime> result{};     for(const auto&amp; key: m_storage.allKeys())         result[QUuid::fromString(key)] = m_storage.value(key).toDateTime();      return result; }  QUuid TokenRepository::createToken(const QDateTime&amp; expiration) {     removeExpiredTokens();     const auto token = QUuid::createUuid();     m_storage.setValue(token.toString(), expiration);     return token; } void TokenRepository::removeToken(const QUuid &amp;token) {     removeExpiredTokens();     m_storage.remove(token.toString()); }  void TokenRepository::removeExpiredTokens() {     const auto current = QDateTime::currentDateTime();     for(const auto&amp; key: m_storage.allKeys())         if(current > m_storage.value(key).toDateTime())             m_storage.remove(key); } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043f\u043e CoreGuidelines \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b <code>isValidToken<\/code> \u0438 <code>tokens<\/code> \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u043d\u044b\u043c\u0438, \u0430 m_storage \u0441\u0434\u0435\u043b\u0430\u0442\u044c mutable, \u0434\u0430\u0431\u044b \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044c \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0438 \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u044c \u0435\u0451 \u043e\u0442 \u0431\u0438\u043d\u0430\u0440\u043d\u043e\u0439, \u043d\u043e \u0442\u0443\u0442 \u044f \u0440\u0435\u0448\u0438\u043b \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<\/div>\n<\/details>\n<h3>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b<\/h3>\n<p>\u042d\u0442\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0443\u0436\u043d\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0447\u0442\u043e\u0431\u044b \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 json \u0432 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438. \u041d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e.<\/p>\n<p>\u0422\u0443\u0442 \u043c\u044b \u0443\u0436\u0435 \u0443\u0432\u0438\u0434\u0438\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 QHttpServerResponse. \u042d\u0442\u043e \u043a\u043b\u0430\u0441\u0441, \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b\u0439 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0431\u0430\u0439\u0442\u043e\u0432, \u0441\u0442\u0440\u043e\u043a\u0443, json, \u043a\u043e\u0440\u043e\u0447\u0435 \u0432\u0441\u0451, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0443\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u0438\u0439 http-\u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f ImageController<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>ImageController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { class ImageController { private:     std::shared_ptr&lt;storages::ImageRepository> m_images; public:     ImageController(const std::shared_ptr&lt;storages::ImageRepository> &amp;images); public:     QHttpServerResponse image(const QString&amp; name) const;     QHttpServerResponse imagesList() const; }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>ImageController.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { ImageController::ImageController(const std::shared_ptr&lt;storages::ImageRepository> &amp;images)     :m_images{ std::move(images) } {}  QHttpServerResponse ImageController::image(const QString &amp;name) const {     QByteArray result{};     \/\/QBuffer - \u043f\u0440\u043e\u0441\u0442\u043e\u0439 QIODevice \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 QByteArray     QBuffer buffer{ &amp;result };     m_images->image(name).save(&amp;buffer, \"PNG\");     return QHttpServerResponse{ result }; } QHttpServerResponse ImageController::imagesList() const {     return QHttpServerResponse{ QJsonArray::fromStringList(m_images->images()) }; } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f TokenController<\/summary>\n<div class=\"spoiler__content\">\n<details class=\"spoiler\">\n<summary>TokenController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { class TokenController { private:     std::shared_ptr&lt;storages::TokenRepository> m_tokens; public:     TokenController(const std::shared_ptr&lt;storages::TokenRepository> &amp;tokens); public:     QHttpServerResponse createToken(quint64 expirationSpan);     QHttpServerResponse removeToken(const QByteArray&amp; token);     QHttpServerResponse getAllTokens(); }; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenController.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace controllers { TokenController::TokenController(const std::shared_ptr&lt;storages::TokenRepository> &amp;tokens)     :m_tokens{ std::move(tokens) } { }  QHttpServerResponse TokenController::createToken(quint64 expirationSpan) {     return QHttpServerResponse{ m_tokens->createToken(QDateTime::currentDateTime().addSecs(expirationSpan)).toString() }; } QHttpServerResponse TokenController::removeToken(const QByteArray &amp;bearer) {     m_tokens->removeToken(QUuid::fromString(bearer));     return QHttpServerResponse{ QHttpServerResponse::StatusCode::Accepted }; } QHttpServerResponse TokenController::getAllTokens() {     QJsonObject result{};     const auto elements = m_tokens->tokens();     for(auto iter = elements.begin(); iter != elements.end(); ++iter)         result[iter.key().toString(QUuid::StringFormat::WithoutBraces)] = iter.value().toSecsSinceEpoch();      return result; } }<\/code><\/pre>\n<p>\u0414<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b json-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430, \u0432 \u043c\u0435\u0442\u043e\u0434 createToken \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u043e \u0441\u0435\u043a\u0443\u043d\u0434, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u043e\u043a\u0435\u043d \u0431\u0443\u0434\u0435\u0442 \u0436\u0438\u0442\u044c: \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043a\u0435\u043d, \u0436\u0438\u0432\u0443\u0449\u0438\u0439 \u0441\u0443\u0442\u043a\u0438, \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c 24 * 60 * 60 = 86400.<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e, \u044d\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0438\u0447\u0435\u0433\u043e, \u043a\u0440\u043e\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 json-\u043e\u0432.   <\/p>\n<h3>Http-\u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f<\/h3>\n<p>\u041d\u0443 \u0438, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u0441\u0430\u043c\u0430\u044f \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<p>\u041c\u044b \u0440\u0435\u0448\u0438\u043b\u0438, \u0447\u0442\u043e \u0434\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043b\u044e\u0431\u043e\u043c\u0443, \u0443 \u043a\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443, \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b, \u044d\u0442\u043e\u0442 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0431\u0443\u0434\u0435\u0442 \u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u043e \u043d\u0430 \u0432\u044b\u0434\u0430\u0447\u0443 \u0442\u043e\u043a\u0435\u043d\u043e\u0432.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0444\u0438\u043b\u044c\u0442\u0440. \u0414\u043b\u044f \u043d\u0435\u0433\u043e \u0432\u0432\u0435\u0434\u0451\u043c \u0434\u0432\u0430 \u043a\u043b\u0430\u0441\u0441\u0430:<\/p>\n<ul>\n<li>\n<p>AbstractHttpController \u2014 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 N \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 QHttpServerResponse.<\/p>\n<\/li>\n<li>\n<p>TokenAuthorizator \u2014 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0444\u0438\u043b\u044c\u0442\u0440, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 N \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 QHttpServerRequest \u043d\u0430 \u043a\u043e\u043d\u0446\u0435.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>AbstractHttpController.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace utils { template&lt;typename...Args> struct AbstractHttpController {     Q_DISABLE_COPY(AbstractHttpController);     virtual QHttpServerResponse handle(const Args&amp;...) = 0;     virtual ~AbstractHttpController() = default;     AbstractHttpController() = default; }; }<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0441\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c \u0438 \u0434\u0435\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c \u0438 \u043c\u0435\u0442\u043e\u0434\u043e\u043c handle, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u043c variadic template.<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>TokenAuthorizator.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">namespace utils { template&lt;typename...Args> class TokenAuthorizator: public AbstractHttpController&lt;Args..., QHttpServerRequest> { private:     std::function&lt;QHttpServerResponse(const Args&amp;...)> m_next;     std::shared_ptr&lt;storages::TokenRepository> m_tokens; public:     TokenAuthorizator(std::shared_ptr&lt;storages::TokenRepository> tokens, const std::function&lt;QHttpServerResponse(const Args&amp;...)>&amp; next)         :m_next{ std::move(next) }, m_tokens{ tokens } {} public:     virtual QHttpServerResponse handle(const Args&amp;...parametes, const QHttpServerRequest&amp; request) override {         if(not request.remoteAddress().isLoopback())             if(not m_tokens->isValidToken(QUuid::fromString(request.value(\"Authorization\"))))                 return QHttpServerResponse::StatusCode::Unauthorized;          return m_next(parametes...);     } }; }<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0443\u0436\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u041a\u043b\u0430\u0441\u0441 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 Args&#8230; \u0438 QHttpServerRequest. Args&#8230; \u043e\u043d \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0434\u0430\u043b\u044c\u0448\u0435, \u0430 \u043f\u043e QHttpServerRequest \u0434\u0435\u043b\u0430\u0435\u0442 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044e. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043d\u0443\u0436\u043d\u0430 \u0435\u0449\u0451 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 QHttpServerRequest \u0442\u043e\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 handle \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0434\u0451\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0440\u0438\u0448\u0451\u043b \u043d\u0435 \u0441 <strong>::1<\/strong> (\u0441 \u044d\u0442\u043e\u0433\u043e \u041f\u041a), \u0430 \u043e\u0442\u043a\u0443\u0434\u0430-\u0442\u043e \u0438\u0437\u0432\u043d\u0435. \u0418 \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u0435\u0434\u0448\u0438\u0439 \u0438\u0437\u0432\u043d\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f code 401 (Unauthorized).<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c tokens \u0432 TokenAuthorizator \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e. \u0421\u0442\u043e\u0438\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u043a\u043b\u0430\u0434\u043a\u0443 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442\u0430, \u0430 \u0443\u0436\u0435 tokens \u0437\u0430\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c \u0432 \u044d\u0442\u043e\u0442 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442. \u042d\u0442\u043e \u0443\u0434\u0430\u043b\u0438\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u044d\u0442\u0438\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438.<\/p>\n<\/li>\n<\/ol>\n<\/div>\n<\/details>\n<h2>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0451 \u0432 \u043a\u0443\u0447\u0443<\/h2>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043b\u0438\u0448\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u0442\u0438 \u043a\u0443\u0441\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u0435. \u0423\u0434\u043e\u0431\u043d\u043e\u0433\u043e Dependency Injection, \u043a\u0430\u043a \u0432 C#, \u043c\u044b \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u043d\u0435 \u0438\u043c\u0435\u0435\u043c, \u0434\u0430 \u0438 \u0442\u0443\u0442 \u043e\u043d \u043f\u043e \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0438\u0437\u043b\u0438\u0448\u0435\u043d. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u043f\u0440\u044f\u043c\u043e \u0432 main. <\/p>\n<details class=\"spoiler\">\n<summary>main.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">QCoreApplication app{ argc, argv };     if(app.arguments().size() == 2)         qFatal(\"Use app: &lt;app-name> &lt;image-dir> &lt;tokens-storage>\");  auto images = std::make_shared&lt;ImageRepository>(argv[1]); auto tokens = std::make_shared&lt;TokenRepository>(argv[2]); auto server = std::make_shared&lt;QHttpServer>();  auto imageController = std::make_shared&lt;ImageController>(images); auto tokenController = std::make_shared&lt;TokenController>(tokens);  \/\/\u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f<\/code><\/pre>\n<\/div>\n<\/details>\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-392753","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/392753","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=392753"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/392753\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=392753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=392753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=392753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}