{"id":445107,"date":"2025-01-15T15:01:23","date_gmt":"2025-01-15T15:01:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=445107"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=445107","title":{"rendered":"<span>Spring Security + Telegram Authentication<\/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>\u041d\u0430\u0447\u0430\u043b \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438 \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 Telegram, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043a\u0440\u043e\u043c\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/848502\/\" rel=\"noopener noreferrer nofollow\">\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0432 Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/a> (\u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0430\u0432\u0442\u043e\u0440\u0443, \u043e\u043d \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0440\u0430\u0431\u043e\u0442\u044b). \u0412\u0442\u043e\u0440\u0443\u044e \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443. \u041f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043a\u043e\u043f\u0430\u0432\u0448\u0438\u0441\u044c \u043f\u0430\u0440\u0443 \u0434\u043d\u0435\u0439 \u0445\u043e\u0447\u0443 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0430\u043c &#171;\u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u043e\u0435&#187; \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f<\/p>\n<p><span class=\"habrahidden\">\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u0432\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 (\u043d\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c \u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e)<\/span><\/p>\n<h2>\u041d\u0430\u0447\u0430\u043b\u043e<\/h2>\n<p>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e: <\/p>\n<ol>\n<li>\n<p>Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 Spring Security<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 PostgreSQL)<\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e <a href=\"https:\/\/core.telegram.org\/widgets\/login\" rel=\"noopener noreferrer nofollow\">https:\/\/core.telegram.org\/widgets\/login<\/a><\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e <a href=\"https:\/\/habr.com\/ru\/articles\/848502\/\" rel=\"noopener noreferrer nofollow\">https:\/\/habr.com\/ru\/articles\/848502\/<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u043e\u0442\u0430<\/p>\n<\/li>\n<\/ol>\n<h2>Telegram Auth<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c html \u0444\u043e\u0440\u043c\u0443 \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u043c \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u043f\u043e \u043f\u0443\u0442\u0438 <code>\/resources\/static\/telegramAuth.html<\/code><\/p>\n<p>\u0421\u0430\u043c\u0430 \u043f\u043e \u0441\u0435\u0431\u0435 \u0444\u043e\u0440\u043c\u0430 \u0441\u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0430\u0434\u0440\u0435\u0441 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435, \u043d\u043e, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0447\u0443\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e:<\/p>\n<p><strong>telegramAuth.html<\/strong><\/p>\n<pre><code class=\"xml\">&lt;!--&lt;script async src=\"https:\/\/telegram.org\/js\/telegram-widget.js?22\" data-telegram-login=\"DynamicQrBot\" data-size=\"large\"--&gt; &lt;!--        data-onauth=\"onTelegramAuth(user)\" data-request-access=\"write\"&gt;&lt;\/script&gt;--&gt; &lt;script type=\"text\/javascript\"&gt;      \/\/localhost     onTelegramAuth(null)     function onTelegramAuth(user) {         fetch(             `http:\/\/localhost:8080\/login`,             {                 method: 'POST',                 headers: {                     'Content-Type': 'application\/json'                 },                 body: JSON.stringify({                     \"id\": \"1\",                     \"first_name\": \"Vasya\",                     \"last_name\": \"Pupkin\",                     \"photo_url\": \"https:\/\/image\",                     \"auth_date\": null,                     \"hash\": \"some-hash\",                     \"username\": \"alekseiiagn\",                 })             }         )     }      \/\/ prod     \/\/ function onTelegramAuth(user) {     \/\/     fetch(     \/\/         `https:\/\/${your - domain}\/login`,     \/\/         {     \/\/             method: 'POST',     \/\/             headers: {     \/\/                 'Content-Type': 'application\/json'     \/\/             },     \/\/             body: JSON.stringify(user)     \/\/         }     \/\/     )     \/\/ } &lt;\/script&gt;<\/code><\/pre>\n<p><u>\u0414\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u0434 <\/u><strong><u>test<\/u><\/strong><u> \u043d\u0430 <\/u><strong><u>prod<\/u><\/strong><u> \u0438 \u0440\u0430\u0441\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c 1-2 \u0441\u0442\u0440\u043e\u0447\u043a\u0443<\/u><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 Spring Security <code>GET \/login<\/code> \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0444\u043e\u0440\u043c\u0443:<\/p>\n<p><strong>TmpAuthController.java<\/strong> (\u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0444\u043e\u0440\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442)<\/p>\n<pre><code class=\"java\">@RestController @RequestMapping(\"\/login\") @RequiredArgsConstructor public class TmpAuthController {      @GetMapping     public ResponseEntity&lt;Resource&gt; getAuthScript() {         var resource = new ClassPathResource(\"\/static\/telegramAuth.html\");         var headers = new HttpHeaders();         headers.add(HttpHeaders.CONTENT_DISPOSITION, \"inline; filename=telegramAuth.html\");         return ResponseEntity.ok()                 .headers(headers)                 .body(resource);     } }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u043e, \u043a\u0430\u043a \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044be \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u0432 <code>POST \/login<\/code> , \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438:<\/p>\n<p><strong>TelegramAuthService.java<\/strong><\/p>\n<pre><code class=\"java\">@Slf4j @Service public class TelegramAuthService {      @Value(\"${TG_BOT_TOKEN}\")     private String tgBotToken;      public boolean isDataValid(Map&lt;String, Object&gt; telegramData) {         var hash = getHash(telegramData);         var dataCheckString = createDataCheckString(telegramData);         try {             var digest = MessageDigest.getInstance(\"SHA-256\");             var key = digest.digest(tgBotToken.getBytes(StandardCharsets.UTF_8));              var hmac = Mac.getInstance(\"HmacSHA256\");             var secretKeySpec = new SecretKeySpec(key, \"HmacSHA256\");             hmac.init(secretKeySpec);              var hmacBytes = hmac.doFinal(dataCheckString.getBytes(StandardCharsets.UTF_8));             var validateHash = new StringBuilder();             for (byte b : hmacBytes) {                 validateHash.append(String.format(\"%02x\", b));             }              return hash.contentEquals(validateHash);         } catch (NoSuchAlgorithmException | InvalidKeyException e) {             log.error(\"Error while authenticate: {}\", e.getMessage());             return false;         }     }      private String getHash(Map&lt;String, Object&gt; telegramData) {         var hash = (String) telegramData.get(\"hash\");         telegramData.remove(\"hash\");         return hash;     }      \/**      * Create a verification line - sort all the parameters and combine them into a line like:      * auth_date=&lt;auth_date&gt;\\nfirst_name=&lt;first_name&gt;\\nid=&lt;id&gt;\\nusername=&lt;username&gt;      *\/     private String createDataCheckString(Map&lt;String, Object&gt; telegramData) {         var sb = new StringBuilder();         telegramData.entrySet().stream()                 .sorted(Map.Entry.comparingByKey())                 .forEach(entry -&gt; sb.append(entry.getKey()).append(\"=\").append(entry.getValue()).append(\"\\n\"));         sb.deleteCharAt(sb.length() - 1);         return sb.toString();     } }<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043d\u0430\u043c \u0442\u0430\u043a \u0436\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0442\u043e\u043a\u0435\u043d \u0431\u043e\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0440\u0438\u0448\u0435\u0434\u0448\u0438\u0435 \u043e\u0442 Telegram<\/p>\n<h2>Spring Security + \u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0423 Spring Security \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 &#8212; <code>UserDetails.java<\/code>. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439:<\/p>\n<p><strong>TelegramUser.java<\/strong><\/p>\n<pre><code class=\"java\">@Getter @Setter @Entity @Table(name = \"users\") public class TelegramUser implements UserDetails {      public static final List&lt;SimpleGrantedAuthority&gt; DEFAULT_AUTHORITIES =             List.of(new SimpleGrantedAuthority(\"USER\"));     public static final String DEFAULT_PASSWORD = \"No password\";      @Id     private String username;     private String telegramId;     private String firstName;     private String lastName;     private String photoUrl;      public TelegramUser(             String telegramId,             String username,             String firstName,             String lastName,             String photoUrl     ) {         this.telegramId = telegramId;         this.username = username;         this.firstName = firstName;         this.lastName = lastName;         this.photoUrl = photoUrl;     }      public TelegramUser() {     }      @Override     public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {         return DEFAULT_AUTHORITIES;     }      @Override     public String getPassword() {         return DEFAULT_PASSWORD;     } }<\/code><\/pre>\n<p><u>username \u043c\u043e\u0436\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0432\u043e\u0439 id, \u043c\u044b \u0436\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u043c username<\/u><\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c Repository \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u043e\u0439:<\/p>\n<p><strong>TelegramUserRepository.java<\/strong><\/p>\n<pre><code class=\"java\">package ru.alekseiiagn.telegramauth.auth.dao;  import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;  import java.util.Optional;  @Repository public interface TelegramUserRepository extends JpaRepository&lt;TelegramUser, String&gt; { }<\/code><\/pre>\n<p>\u0423 Spring Security \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>UserDetailsManager.java<\/code> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <code>UserDetails<\/code>, \u043d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u0441\u0432\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0442\u043e \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438 \u0441\u0432\u043e\u0439 Manager:<\/p>\n<p><strong>TelegramUserDetailsManager.java<\/strong><\/p>\n<pre><code class=\"java\">@RequiredArgsConstructor public class TelegramUserDetailsManager implements UserDetailsManager {      private final TelegramUserRepository telegramUserRepository;      @Override     public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {         return telegramUserRepository.findById(id)                 .orElseThrow(() -&gt; new UsernameNotFoundException(\"User not found\"));     }      \/**      * On a repeat call, the user's data will be updated      *\/     @Override     public void createUser(UserDetails user) {         telegramUserRepository.save((TelegramUser) user);     }      @Override     public void deleteUser(String id) {         telegramUserRepository.deleteById(id);     }      @Override     public boolean userExists(String id) {         return telegramUserRepository.findById(id).isPresent();     }      @Override     public void updateUser(UserDetails user) {         \/* Not implemented *\/     }      @Override     public void changePassword(String oldPassword, String newPassword) {         \/* Not implemented *\/     } }<\/code><\/pre>\n<h2>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 Spring Security<\/h2>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u043e, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 Spring Security:<\/p>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>POST \/login<\/code> (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0441\u0430\u043c\u0438\u043c Spring Security)<\/p>\n<\/li>\n<li>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0444\u0438\u043b\u044c\u0442\u0440 <code>AbstractAuthenticationProcessingFilter.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 <code>Authentication.java<\/code><\/p>\n<\/li>\n<li>\n<p>\u041e\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>AuthenticationManager.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>ProviderManager.java<\/code><\/p>\n<\/li>\n<li>\n<p>\u0412 <code>ProviderManager.java<\/code>  \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 <code>AuthenticationProvider.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c\u0441\u044f \u0432\u0432\u0435\u0440\u0445 \u0438 <code>AbstractAuthenticationProcessingFilter.java<\/code> \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442 \u0432 <strong>Spring Context<\/strong> \u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u0432\u044b\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f Cookie<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bdb\/cc4\/531\/bdbcc45311c0935f23f0c14518a1acce.png\" alt=\"\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b Spring Security\" title=\"\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b Spring Security\" width=\"611\" height=\"671\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bdb\/cc4\/531\/bdbcc45311c0935f23f0c14518a1acce.png\"\/><\/p>\n<div><figcaption>\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b Spring Security<\/figcaption><\/div>\n<\/figure>\n<p>\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u0442\u044c \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0432\u044b\u0448\u0435\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0435:<\/p>\n<p><strong>TelegramAuthToken.java:<\/strong><\/p>\n<pre><code class=\"java\">\/\/ AbstractAuthenticationToken implements Authentication @Getter public class TelegramAuthToken extends AbstractAuthenticationToken {      private final Object principal;     private final Object credentials;      public static TelegramAuthToken unauthenticated(Map&lt;String, Object&gt; data) {         return new TelegramAuthToken(                 data.get(\"id\"),                 data,                 false         );     }      public static TelegramAuthToken authenticated(UserDetails userDetails) {         return new TelegramAuthToken(                 userDetails,                 userDetails,                 true         );     }      private TelegramAuthToken(             Object principal,             Object credentials,             boolean authenticated     ) {         super(                 TelegramUser.DEFAULT_AUTHORITIES         );         this.principal = principal;         this.credentials = credentials;         setAuthenticated(authenticated);     }      @Override     public Object getCredentials() {         return credentials;     }      @Override     public Object getPrincipal() {         return principal;     } }<\/code><\/pre>\n<p><strong>TelegramAuthFilter.java<\/strong><\/p>\n<pre><code class=\"java\">@Slf4j @RequiredArgsConstructor public class TelegramUserDetailsAuthProvider implements AuthenticationProvider {      private final TelegramAuthService telegramAuthService;     private final UserDetailsManager userDetailsManager;      @Override     public Authentication authenticate(Authentication authentication) throws AuthenticationException {         var data = (Map&lt;String, Object&gt;) authentication.getCredentials();         try {             if (true) { \/\/for localhost usage \/\/            if (telegramAuthService.isDataValid(data)) { \/\/for prod                 var telegramUser = new TelegramUser(                         (String) authentication.getPrincipal(),                         getStringValue(data, \"username\"),                         getStringValue(data, \"first_name\"),                         getStringValue(data, \"last_name\"),                         getStringValue(data, \"photo_url\")                 );                 log.info(\"Successfully checked user {} data\", telegramUser.getTelegramId());                 upsertUser(telegramUser);                 var userDetails = userDetailsManager.loadUserByUsername(telegramUser.getUsername());                 return TelegramAuthToken.authenticated(userDetails);             } else {                 throw new AuthenticationServiceException(\"Data is not valid\");             }         } catch (UsernameNotFoundException notFound) {             throw notFound;         } catch (Exception repositoryProblem) {             throw new InternalAuthenticationServiceException(                     repositoryProblem.getMessage(),                     repositoryProblem             );         }     }      private void upsertUser(UserDetails user) {         if (userDetailsManager.userExists(user.getUsername())) {             userDetailsManager.updateUser(user);         } else {             userDetailsManager.createUser(user);         }     }      private static String getStringValue(Map&lt;String, Object&gt; requestBody, String key) {         var value = requestBody.get(key);         return (value != null)                 ? value.toString().trim()                 : \"\";     }      @Override     public boolean supports(Class&lt;?&gt; authentication) {         return true;     } }<\/code><\/pre>\n<p><strong>TelegramUserDetailsManager.java<\/strong><\/p>\n<pre><code class=\"java\">@Slf4j @RequiredArgsConstructor public class TelegramUserDetailsAuthProvider implements AuthenticationProvider {      private final TelegramAuthService telegramAuthService;     private final UserDetailsManager userDetailsManager;      @Override     public Authentication authenticate(Authentication authentication) throws AuthenticationException {         var data = (Map&lt;String, Object&gt;) authentication.getCredentials();         try {             if (true) { \/\/for localhost usage \/\/            if (telegramAuthService.isDataValid(data)) { \/\/for prod                 var telegramUser = new TelegramUser(                         (String) authentication.getPrincipal(),                         getStringValue(data, \"username\"),                         getStringValue(data, \"first_name\"),                         getStringValue(data, \"last_name\"),                         getStringValue(data, \"photo_url\")                 );                 log.info(\"Successfully checked user {} data\", telegramUser.getTelegramId());                 upsertUser(telegramUser);                 var userDetails = userDetailsManager.loadUserByUsername(telegramUser.getUsername());                 return TelegramAuthToken.authenticated(userDetails);             } else {                 throw new AuthenticationServiceException(\"Data is not valid\");             }         } catch (UsernameNotFoundException notFound) {             throw notFound;         } catch (Exception repositoryProblem) {             throw new InternalAuthenticationServiceException(                     repositoryProblem.getMessage(),                     repositoryProblem             );         }     }      private void upsertUser(UserDetails user) {         if (userDetailsManager.userExists(user.getUsername())) {             userDetailsManager.updateUser(user);         } else {             userDetailsManager.createUser(user);         }     }      private static String getStringValue(Map&lt;String, Object&gt; requestBody, String key) {         var value = requestBody.get(key);         return (value != null)                 ? value.toString().trim()                 : \"\";     }      @Override     public boolean supports(Class&lt;?&gt; authentication) {         return true;     } }<\/code><\/pre>\n<p><strong>TelegramUserDetailsAuthProvider.java<\/strong><\/p>\n<pre><code class=\"java\">@Slf4j @RequiredArgsConstructor public class TelegramUserDetailsAuthProvider implements AuthenticationProvider {      private final TelegramAuthService telegramAuthService;     private final UserDetailsManager userDetailsManager;      @Override     public Authentication authenticate(Authentication authentication) throws AuthenticationException {         var data = (Map&lt;String, Object&gt;) authentication.getCredentials();         try {             if (true) { \/\/for localhost usage \/\/            if (telegramAuthService.isDataValid(data)) { \/\/for prod                 var telegramUser = new TelegramUser(                         (String) authentication.getPrincipal(),                         getStringValue(data, \"username\"),                         getStringValue(data, \"first_name\"),                         getStringValue(data, \"last_name\"),                         getStringValue(data, \"photo_url\")                 );                 log.info(\"Successfully checked user {} data\", telegramUser.getTelegramId());                 upsertUser(telegramUser);                 var userDetails = userDetailsManager.loadUserByUsername(telegramUser.getUsername());                 return TelegramAuthToken.authenticated(userDetails);             } else {                 throw new AuthenticationServiceException(\"Data is not valid\");             }         } catch (UsernameNotFoundException notFound) {             throw notFound;         } catch (Exception repositoryProblem) {             throw new InternalAuthenticationServiceException(                     repositoryProblem.getMessage(),                     repositoryProblem             );         }     }      private void upsertUser(UserDetails user) {         if (userDetailsManager.userExists(user.getUsername())) {             userDetailsManager.updateUser(user);         } else {             userDetailsManager.createUser(user);         }     }      private static String getStringValue(Map&lt;String, Object&gt; requestBody, String key) {         var value = requestBody.get(key);         return (value != null)                 ? value.toString().trim()                 : \"\";     }      @Override     public boolean supports(Class&lt;?&gt; authentication) {         return true;     } }<\/code><\/pre>\n<p>\u041f\u043e\u0434\u0441\u0432\u0435\u0447\u0443, \u0447\u0442\u043e \u043f\u0440\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 telegram, \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 <code>telegramAuthService.isDataValid(data)<\/code> \u0432 <code>TelegramUserDetailsAuthProvider.java<\/code> <\/p>\n<h2>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f Spring Security<\/h2>\n<p>\u0412\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f &#8212; \u044d\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0431\u0435\u0440\u0435\u0442 \u0432\u043e\u0435\u0434\u0438\u043d\u043e \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0434\u043e \u044d\u0442\u043e\u0433\u043e:<\/p>\n<p><strong>SecurityConfig.java<\/strong><\/p>\n<pre><code class=\"java\">@Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig {      private static final String[] NO_AUTH_URLS = {             \"\/hello-world\/public\",             \"\/login\",     };     private static final String[] AUTH_URLS = {             \"\/hello-world\/private\",     };       @Bean     public SecurityFilterChain securityFilterChain(             HttpSecurity http,             SecurityContextRepository contextRepository,             AuthenticationManager authenticationManager     ) throws Exception {         return http                 .csrf(AbstractHttpConfigurer::disable) \/\/ Disable CSRF for simplicity                 .authorizeHttpRequests(auth -&gt; auth                         .requestMatchers(NO_AUTH_URLS).permitAll()                         .requestMatchers(AUTH_URLS).authenticated()                         .anyRequest().authenticated()                 )                 .formLogin(formLogin -&gt; formLogin                         .loginPage(\"\/login\")                         .loginProcessingUrl(\"\/login\")                         .permitAll()                 )                 .addFilterAt(                         new TelegramAuthFilter(contextRepository, authenticationManager),                         UsernamePasswordAuthenticationFilter.class                 )                 .build();     }      @Bean     public UserDetailsManager userDetailsManager(             TelegramUserRepository telegramUserRepository     ) {         return new TelegramUserDetailsManager(telegramUserRepository);     }      @Bean     public SecurityContextRepository securityContextRepository() {         return new HttpSessionSecurityContextRepository();     }      @Bean     public AuthenticationManager authenticationManager(             AuthenticationProvider telegramAuthProvider,             UserDetailsManager userDetailsManager     ) {         DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();         authenticationProvider.setUserDetailsService(userDetailsManager);         ProviderManager providerManager = new ProviderManager(telegramAuthProvider);         providerManager.setEraseCredentialsAfterAuthentication(false);         return providerManager;     }      @Bean     public AuthenticationProvider telegramAuthProvider(             TelegramAuthService telegramAuthService,             UserDetailsManager userDetailsManager     ) {         return new TelegramUserDetailsAuthProvider(                 telegramAuthService,                 userDetailsManager         );     } }<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0435 \u0432\u0441\u0435\u043c (<code>\/hello-world\/public<\/code>) \u0438 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 (<code>\/hello-world\/private<\/code>) API, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u0422\u0430\u043a \u0436\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043f\u0443\u0442\u044c \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 <code>\/login<\/code>, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432 Spring Security<\/p>\n<h2>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440:<\/p>\n<pre><code class=\"java\">@RestController @RequestMapping(\"\/hello-world\") @RequiredArgsConstructor public class HelloWorldController {      @GetMapping(\"\/public\")     public String helloWorld() {         return \"Hello World\";     }      @GetMapping(\"\/private\")     public String helloWorldPerson(             @AuthenticationPrincipal TelegramUser user     ) {         return \"Hello World, \" + user.getUsername();     } } <\/code><\/pre>\n<p>\u0428\u0430\u0433\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/454\/bab\/477\/454bab47730e26c1397b184f125d0687.png\" alt=\"\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430\" title=\"\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430\" width=\"644\" height=\"166\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/454\/bab\/477\/454bab47730e26c1397b184f125d0687.png\"\/><\/p>\n<div><figcaption>\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u043d\u0435\u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430<\/figcaption><\/div>\n<\/figure>\n<\/li>\n<li>\n<p>\u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043d\u0430\u0441 \u043f\u0435\u0440\u0435\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u0442 \/login<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/12a\/a30\/f3c\/12aa30f3ce0e429d923ad4c45c024923.png\" alt=\"\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login\" title=\"\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login\" width=\"526\" height=\"210\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/12a\/a30\/f3c\/12aa30f3ce0e429d923ad4c45c024923.png\"\/><\/p>\n<div><figcaption>\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login<\/figcaption><\/div>\n<\/figure>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d (\u0435\u0441\u043b\u0438 localhost, \u0442\u043e \u043e\u043d\u0430 \u043f\u0440\u043e\u0439\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438)<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f90\/eea\/85e\/f90eea85e18fb8f2fade69061e8b16df.png\" alt=\"\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login \u043f\u043e\u0441\u043b\u0435 log in\" title=\"\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login \u043f\u043e\u0441\u043b\u0435 log in\" width=\"566\" height=\"224\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f90\/eea\/85e\/f90eea85e18fb8f2fade69061e8b16df.png\"\/><\/p>\n<div><figcaption>\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \/login \u043f\u043e\u0441\u043b\u0435 log in<\/figcaption><\/div>\n<\/figure>\n<\/li>\n<li>\n<p>\u0421\u043d\u043e\u0432\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f49\/3c6\/187\/f493c618727f2609c3a0c8e44b54330d.png\" alt=\"\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e\u0441\u043b\u0435 log in\" title=\"\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e\u0441\u043b\u0435 log in\" width=\"672\" height=\"234\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f49\/3c6\/187\/f493c618727f2609c3a0c8e44b54330d.png\"\/><\/p>\n<div><figcaption>\u041e\u0442\u0432\u0435\u0442 \u0438\u0437 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e\u0441\u043b\u0435 log in<\/figcaption><\/div>\n<\/figure>\n<\/li>\n<\/ol>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c \u044f \u0445\u043e\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043c\u043e\u0433 \u0432\u0430\u043c, \u0441\u043f\u0430\u0441\u0438\u0431\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b\u0438, \u0443\u0432\u0438\u0434\u0438\u043c\u0441\u044f \u0432 \u043d\u043e\u0432\u044b\u0445 \u0441\u0442\u0430\u0442\u044f\u0445 \ud83d\ude42<\/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\/873786\/\"> https:\/\/habr.com\/ru\/articles\/873786\/<\/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>\u041d\u0430\u0447\u0430\u043b \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438 \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 Telegram, \u043d\u043e \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043a\u0440\u043e\u043c\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/848502\/\" rel=\"noopener noreferrer nofollow\">\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0432 Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438<\/a> (\u0441\u043f\u0430\u0441\u0438\u0431\u043e \u0430\u0432\u0442\u043e\u0440\u0443, \u043e\u043d \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0440\u0430\u0431\u043e\u0442\u044b). \u0412\u0442\u043e\u0440\u0443\u044e \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443. \u041f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043a\u043e\u043f\u0430\u0432\u0448\u0438\u0441\u044c \u043f\u0430\u0440\u0443 \u0434\u043d\u0435\u0439 \u0445\u043e\u0447\u0443 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0430\u043c &#171;\u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u043e\u0435&#187; \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f<\/p>\n<p><span class=\"habrahidden\">\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e, \u0432\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0437\u0430\u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 (\u043d\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u043e\u0442\u0435\u0441\u0442\u0438\u0442\u044c \u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e)<\/span><\/p>\n<h2>\u041d\u0430\u0447\u0430\u043b\u043e<\/h2>\n<p>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e: <\/p>\n<ol>\n<li>\n<p>Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 Spring Security<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0432 \u043c\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 PostgreSQL)<\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e <a href=\"https:\/\/core.telegram.org\/widgets\/login\" rel=\"noopener noreferrer nofollow\">https:\/\/core.telegram.org\/widgets\/login<\/a><\/p>\n<\/li>\n<li>\n<p>\u0418\u0437\u0443\u0447\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e <a href=\"https:\/\/habr.com\/ru\/articles\/848502\/\" rel=\"noopener noreferrer nofollow\">https:\/\/habr.com\/ru\/articles\/848502\/<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u043e\u0442\u0430<\/p>\n<\/li>\n<\/ol>\n<h2>Telegram Auth<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c html \u0444\u043e\u0440\u043c\u0443 \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u043c \u0432 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u043f\u043e \u043f\u0443\u0442\u0438 <code>\/resources\/static\/telegramAuth.html<\/code><\/p>\n<p>\u0421\u0430\u043c\u0430 \u043f\u043e \u0441\u0435\u0431\u0435 \u0444\u043e\u0440\u043c\u0430 \u0441\u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0430\u0434\u0440\u0435\u0441 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435, \u043d\u043e, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0447\u0443\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e:<\/p>\n<p><strong>telegramAuth.html<\/strong><\/p>\n<pre><code class=\"xml\">&lt;!--&lt;script async src=\"https:\/\/telegram.org\/js\/telegram-widget.js?22\" data-telegram-login=\"DynamicQrBot\" data-size=\"large\"--&gt; &lt;!--        data-onauth=\"onTelegramAuth(user)\" data-request-access=\"write\"&gt;&lt;\/script&gt;--&gt; &lt;script type=\"text\/javascript\"&gt;      \/\/localhost     onTelegramAuth(null)     function onTelegramAuth(user) {         fetch(             `http:\/\/localhost:8080\/login`,             {                 method: 'POST',                 headers: {                     'Content-Type': 'application\/json'                 },                 body: JSON.stringify({                     \"id\": \"1\",                     \"first_name\": \"Vasya\",                     \"last_name\": \"Pupkin\",                     \"photo_url\": \"https:\/\/image\",                     \"auth_date\": null,                     \"hash\": \"some-hash\",                     \"username\": \"alekseiiagn\",                 })             }         )     }      \/\/ prod     \/\/ function onTelegramAuth(user) {     \/\/     fetch(     \/\/         `https:\/\/${your - domain}\/login`,     \/\/         {     \/\/             method: 'POST',     \/\/             headers: {     \/\/                 'Content-Type': 'application\/json'     \/\/             },     \/\/             body: JSON.stringify(user)     \/\/         }     \/\/     )     \/\/ } &lt;\/script&gt;<\/code><\/pre>\n<p><u>\u0414\u043b\u044f \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u0434 <\/u><strong><u>test<\/u><\/strong><u> \u043d\u0430 <\/u><strong><u>prod<\/u><\/strong><u> \u0438 \u0440\u0430\u0441\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c 1-2 \u0441\u0442\u0440\u043e\u0447\u043a\u0443<\/u><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 Spring Security <code>GET \/login<\/code> \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0444\u043e\u0440\u043c\u0443:<\/p>\n<p><strong>TmpAuthController.java<\/strong> (\u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0444\u043e\u0440\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442)<\/p>\n<pre><code class=\"java\">@RestController @RequestMapping(\"\/login\") @RequiredArgsConstructor public class TmpAuthController {      @GetMapping     public ResponseEntity&lt;Resource&gt; getAuthScript() {         var resource = new ClassPathResource(\"\/static\/telegramAuth.html\");         var headers = new HttpHeaders();         headers.add(HttpHeaders.CONTENT_DISPOSITION, \"inline; filename=telegramAuth.html\");         return ResponseEntity.ok()                 .headers(headers)                 .body(resource);     } }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u043e, \u043a\u0430\u043a \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044be \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u0432 <code>POST \/login<\/code> , \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438:<\/p>\n<p><strong>TelegramAuthService.java<\/strong><\/p>\n<pre><code class=\"java\">@Slf4j @Service public class TelegramAuthService {      @Value(\"${TG_BOT_TOKEN}\")     private String tgBotToken;      public boolean isDataValid(Map&lt;String, Object&gt; telegramData) {         var hash = getHash(telegramData);         var dataCheckString = createDataCheckString(telegramData);         try {             var digest = MessageDigest.getInstance(\"SHA-256\");             var key = digest.digest(tgBotToken.getBytes(StandardCharsets.UTF_8));              var hmac = Mac.getInstance(\"HmacSHA256\");             var secretKeySpec = new SecretKeySpec(key, \"HmacSHA256\");             hmac.init(secretKeySpec);              var hmacBytes = hmac.doFinal(dataCheckString.getBytes(StandardCharsets.UTF_8));             var validateHash = new StringBuilder();             for (byte b : hmacBytes) {                 validateHash.append(String.format(\"%02x\", b));             }              return hash.contentEquals(validateHash);         } catch (NoSuchAlgorithmException | InvalidKeyException e) {             log.error(\"Error while authenticate: {}\", e.getMessage());             return false;         }     }      private String getHash(Map&lt;String, Object&gt; telegramData) {         var hash = (String) telegramData.get(\"hash\");         telegramData.remove(\"hash\");         return hash;     }      \/**      * Create a verification line - sort all the parameters and combine them into a line like:      * auth_date=&lt;auth_date&gt;\\nfirst_name=&lt;first_name&gt;\\nid=&lt;id&gt;\\nusername=&lt;username&gt;      *\/     private String createDataCheckString(Map&lt;String, Object&gt; telegramData) {         var sb = new StringBuilder();         telegramData.entrySet().stream()                 .sorted(Map.Entry.comparingByKey())                 .forEach(entry -&gt; sb.append(entry.getKey()).append(\"=\").append(entry.getValue()).append(\"\\n\"));         sb.deleteCharAt(sb.length() - 1);         return sb.toString();     } }<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043d\u0430\u043c \u0442\u0430\u043a \u0436\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0442\u043e\u043a\u0435\u043d \u0431\u043e\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0440\u0438\u0448\u0435\u0434\u0448\u0438\u0435 \u043e\u0442 Telegram<\/p>\n<h2>Spring Security + \u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0423 Spring Security \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 &#8212; <code>UserDetails.java<\/code>. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439:<\/p>\n<p><strong>TelegramUser.java<\/strong><\/p>\n<pre><code class=\"java\">@Getter @Setter @Entity @Table(name = \"users\") public class TelegramUser implements UserDetails {      public static final List&lt;SimpleGrantedAuthority&gt; DEFAULT_AUTHORITIES =             List.of(new SimpleGrantedAuthority(\"USER\"));     public static final String DEFAULT_PASSWORD = \"No password\";      @Id     private String username;     private String telegramId;     private String firstName;     private String lastName;     private String photoUrl;      public TelegramUser(             String telegramId,             String username,             String firstName,             String lastName,             String photoUrl     ) {         this.telegramId = telegramId;         this.username = username;         this.firstName = firstName;         this.lastName = lastName;         this.photoUrl = photoUrl;     }      public TelegramUser() {     }      @Override     public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {         return DEFAULT_AUTHORITIES;     }      @Override     public String getPassword() {         return DEFAULT_PASSWORD;     } }<\/code><\/pre>\n<p><u>username \u043c\u043e\u0436\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043b\u0443\u0447\u0448\u0435 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0432\u043e\u0439 id, \u043c\u044b \u0436\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u043c username<\/u><\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c Repository \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0431\u0430\u0437\u043e\u0439:<\/p>\n<p><strong>TelegramUserRepository.java<\/strong><\/p>\n<pre><code class=\"java\">package ru.alekseiiagn.telegramauth.auth.dao;  import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;  import java.util.Optional;  @Repository public interface TelegramUserRepository extends JpaRepository&lt;TelegramUser, String&gt; { }<\/code><\/pre>\n<p>\u0423 Spring Security \u0435\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>UserDetailsManager.java<\/code> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 <code>UserDetails<\/code>, \u043d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u0441\u0432\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0442\u043e \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438 \u0441\u0432\u043e\u0439 Manager:<\/p>\n<p><strong>TelegramUserDetailsManager.java<\/strong><\/p>\n<pre><code class=\"java\">@RequiredArgsConstructor public class TelegramUserDetailsManager implements UserDetailsManager {      private final TelegramUserRepository telegramUserRepository;      @Override     public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {         return telegramUserRepository.findById(id)                 .orElseThrow(() -&gt; new UsernameNotFoundException(\"User not found\"));     }      \/**      * On a repeat call, the user's data will be updated      *\/     @Override     public void createUser(UserDetails user) {         telegramUserRepository.save((TelegramUser) user);     }      @Override     public void deleteUser(String id) {         telegramUserRepository.deleteById(id);     }      @Override     public boolean userExists(String id) {         return telegramUserRepository.findById(id).isPresent();     }      @Override     public void updateUser(UserDetails user) {         \/* Not implemented *\/     }      @Override     public void changePassword(String oldPassword, String newPassword) {         \/* Not implemented *\/     } }<\/code><\/pre>\n<h2>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 Spring Security<\/h2>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u043e\u0440\u043e\u0442\u043a\u043e, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 Spring Security:<\/p>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>POST \/login<\/code> (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0441\u0430\u043c\u0438\u043c Spring Security)<\/p>\n<\/li>\n<li>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0444\u0438\u043b\u044c\u0442\u0440 <code>AbstractAuthenticationProcessingFilter.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 <code>Authentication.java<\/code><\/p>\n<\/li>\n<li>\n<p>\u041e\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>AuthenticationManager.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>ProviderManager.java<\/code><\/p>\n<\/li>\n<li>\n<p>\u0412 <code>ProviderManager.java<\/code>  \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0438 <code>AuthenticationProvider.java<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043f\u043e \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u043c\u0441\u044f \u0432\u0432\u0435\u0440\u0445 \u0438 <code>AbstractAuthenticationProcessingFilter.java<\/code> \u043f\u043e\u043c\u0435\u0449\u0430\u0435\u0442 \u0432 <strong>Spring Context<\/strong> \u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u0432\u044b\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0430\u044f Cookie<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\">\n<div><figcaption>\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b Spring Security<\/figcaption><\/div>\n<\/figure>\n<p>\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u0442\u044c \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0432\u044b\u0448\u0435\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0435:<\/p>\n<p><strong>TelegramAuthToken.java:<\/strong><\/p>\n<pre><code class=\"java\">\/\/ AbstractAuthenticationToken implements Authentication @Getter public class TelegramAuthToken extends AbstractAuthenticationToken {      private final Object principal;     private final Object credentials;      public static TelegramAuthToken unauthenticated(Map&lt;String, Object&gt; data) {         return new TelegramAuthToken(                 data.get(\"id\"),                 data,                 false         );     }      public static TelegramAuthToken authenticated(UserDetails userDetails) {         return new TelegramAuthToken(                 userDetails,                 userDetails,                 true         );     }      private TelegramAuthToken(             Object principal,             Object credentials,             boolean authenticated     ) {         super(                 TelegramUser.DEFAULT_AUTHORITIES         );         this.principal = principal;         this.credentials = credentials;         setAuthenticated(authenticated);     }      @Override     public Object<\/code><\/pre>\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-445107","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445107","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=445107"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445107\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=445107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=445107"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=445107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}