Создание Spring Security REST API с использованием JWT токена

Поделиться
HTML-код
  • Опубликовано: 8 апр 2019
  • Ссылка на репозиторий с исходным кодом проекта:
    github.com/proselytear/jwtapp...
    Ссылка на документацию Spring Security:
    docs.spring.io/spring-securit...
    CSRF:
    ru.wikipedia.org/wiki/%D0%9C%...
    habr.com/ru/post/318748/
    В данном видео создано REST API с использованием Spring Security. Аутентификация и авторизация реализованы с использованием JWT токена.
    Технологии:
    Java, Spring (IoC, Web, Data, Security), MySQL, Lombok, JsonWebToken, Liquibase, Git.

Комментарии • 413

  • @user-jt5ds8fo6r
    @user-jt5ds8fo6r 4 года назад +36

    Прелесть данного туториала в том, что он не слишком сложный и не слишком простой.
    То есть, это не банальный 'hello world', который не понятно как применять в реальном проекте.
    Также это не перегруженный проект в котором сложно разобраться.
    Золотая середина!
    Очень классно рассказал о Spring Security. Какие компоненты за что отвечают. Наконец-то стало ясно как оно работает и как настраивать.
    П.с. Надеюсь ты уже улучшил свои скилы работы с Optional.

  • @user-lx3ul3ix1t
    @user-lx3ul3ix1t 4 года назад +5

    Очень крутое видео! Спасибо. Единственное полное видео по этой теме, которое смог найти.

  • @egorvakulenko6198
    @egorvakulenko6198 4 года назад +15

    Очень полезное видео! Спасибо! Еще и тему DTO зацепил - вообще Красавчик!

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад

      Спасибо за отзыв!

    • @VladimirDubenco
      @VladimirDubenco 2 года назад

      А еще и показал как правильно аннотировать BaseEntity !

  • @user-zl9bb7po6b
    @user-zl9bb7po6b 4 года назад +2

    Большущее тебе спасибо, бро! Доходчиво, понятно, супер. Очень сильно жизнь облегчило, как раз пишу дипломный по курсах...

  • @arturbarkou6347
    @arturbarkou6347 4 года назад +6

    Все работает, спасибо больше за видео. Было бы круто добавить походу больше пояснений про внутреннее устройство spring security и почему сделано так а не иначе вместо надиктовывания строчек кода.

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад +1

      Спасибо за отзыв!
      По поводу более глубокого объяснения - здесь мне будет крайне сложно превзойти документацию, изучение которой я крайне рекомендую.
      docs.spring.io/spring-security/site/docs/current/reference/html5/
      Цель видео - дать самое базовое представление и практический навык работы с технологией.

  • @user-iu6yz6ck6h
    @user-iu6yz6ck6h 5 лет назад +10

    Спасибо за видео. Не пропадайте, очень интересно!

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +6

      Спасибо за отзыв, Александр. Постараюсь )

    • @k0jihero
      @k0jihero 3 года назад

      @@EugeneSuleimanov Евгений, снимите туториал по микросервисам)

  • @overlordofprogramminglangu7822
    @overlordofprogramminglangu7822 4 года назад +1

    Спасибо большое за объяснение столь важной темы)

  • @user-iu6yz6ck6h
    @user-iu6yz6ck6h 4 года назад +1

    Очень классное виде! Очень жаль, что таких больше не выпускаете

  • @alexhali6003
    @alexhali6003 3 года назад +1

    Все отлично, есть несколько пожеланий:
    1. Autowired над конструктором можно не вешать, т.к. других конструкторов нет и при создании бина Spring сам поймет что инжектить.
    2. Аннотация @AllArgsConstructor, как уже писали - сильно поможет.
    3. В имплементации UserDetails сильно нужны @AllArgsConstructor и @Data, т.к. отвлекает генерация геттеров\сеттеров.
    4. Публичный конструктор без параметров в фабрике не нужен, javac сам подсунет если не найдет.
    5. Было бы неплохо добавить вариант ограничения доступа через аннотации, типа @RolesAllowed/@Secured

  • @user-yn8zr4xv4m
    @user-yn8zr4xv4m 5 лет назад +3

    Спасибо, не пропадайте больше!)

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +4

      Спасибоза отзыв. Постараюсь )

  • @4val0v
    @4val0v 5 лет назад

    Евгений, у вас лучшие ролики, доходчиво и без воды, жаль что видео приходится ждать месяцами :(
    Также мне очень хотелось бы увидеть продолжение данного ролика с использованием mapstruct

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +4

      Спасибо за отзыв, Андрей. Постараюсь выложить вторую часть в течение нескольких недель (обновление токена, регистрация и т.д.)

    • @4val0v
      @4val0v 5 лет назад

      @@EugeneSuleimanov Это хорошие новости, будем ждать, спасибо большое

  • @veli2698
    @veli2698 4 года назад +5

    Подскажите, почему не используете DI для JwtTokenFilter и JwtTokenProvider, а создаете их вручную? Можно ли так же аннотировать эти классы через @Component и заменить все new на инжект через DI?

  • @jakhara4202
    @jakhara4202 2 года назад

    Спасибо вам большое за ваш труд. Лучшей материал на RUclips по этой теме СУПЕР.

  • @user-hb8gv3cu1l
    @user-hb8gv3cu1l 4 года назад +2

    Отлично объясняешь, видео очень помогло, большое человеческое тебе, друг)

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад

      Спасибо за отзыв! Рад, что материал оказался полезен.

  • @user-mz4xd3dp1c
    @user-mz4xd3dp1c Год назад +1

    Спасибо большое! Не смотря на длительность было очень интересно смотреть)

  • @user-sq6kg8pj4w
    @user-sq6kg8pj4w 3 года назад +2

    @
    Eugene Suleimanov Огромное спасибо за данный урок! Подскажите каким образом можно токен сделать недействительным, выполнив logout. Как я понял можно создать сущность "чёрный список" для JWT токенов, но как правильно реализовать до меня не доходит. Или для этого и существует Auth 2.0 и работа с ним? Накиньте пожалуйста мыслей!

  • @olexandr_kiss
    @olexandr_kiss 4 года назад +1

    Не мог не отписать. Очень грамотно сделано и подано. Спасибо.

  • @ErikGhukasyan
    @ErikGhukasyan 4 года назад

    Спасибо большое за видеоурок. Он мне очень помог. Не пропадайте ;)

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +1

      Спасибо за отзыв! Постараюсь)

  • @alexpodshivalov1010
    @alexpodshivalov1010 3 года назад +4

    Превосходное видео и объяснения. Было бы здорово для полноты катрины добавить регистрацию пользователя в этом же проекте. Если есть такое видео, то поделитесь пожалуйста ссылкой

  • @odilbekrahmonov1584
    @odilbekrahmonov1584 3 года назад

    Спасибо за урок мне очень понравилось

  • @iorgankrat1118
    @iorgankrat1118 3 года назад

    спасибо вам, очень круто

  • @ShulV
    @ShulV Год назад

    Не слишком сложно и не слишком легко. То, что нужно!

  • @PP-hn9vq
    @PP-hn9vq 2 года назад

    спасибо за шикарный видос) даже код есть, мечта))) чуть бы код покрупнее.

  • @ololo2274
    @ololo2274 4 года назад +2

    Здравствуйте ) А как интегрировать liquibase в Spring MVC, чтобы при запуске приложения создавались таблицы в БД? Я пытаюсь сконфигурировать плагин со всеми настройками и там указать путь к файлу changelog.xml. Но таблицы не создаются.

  • @engend3405
    @engend3405 Год назад

    Спасибо , как всегда круто)

  • @john_slayer666
    @john_slayer666 7 месяцев назад

    Респект, подача материала просто супер! Единственное замечание, если можно код покрупнее, а так все очень доходчиво.

    • @EugeneSuleimanov
      @EugeneSuleimanov  7 месяцев назад +1

      Спасибо за отзыв и замечание, постараюсь исправить в будущем.

  • @turkmenperson681
    @turkmenperson681 4 года назад

    Спасибо за Урок, лучшая объяснения)) Вопрос, можешь снимать урок где добавляешь возможность logout и remember me???)

  • @eugenevolohonsky1469
    @eugenevolohonsky1469 Год назад +1

    Нашел хорошее видео на ту же тематику, но уши кровоточат от чудовищного каверканья английских терминов. Как же приятно слушать Евгения

  • @belysheva80
    @belysheva80 3 года назад

    Спасибо большое за видео! Подскажите, пожалуйста, а если у нас LDAP авторизация, подход остается тот же?

  • @user-sj9kh4pf7n
    @user-sj9kh4pf7n 5 лет назад

    С возвращением!)

  • @user-wx4rs8ju3h
    @user-wx4rs8ju3h 7 месяцев назад

    Отличный выпуск) спасибо за ваш труд)

  • @delalen8012
    @delalen8012 3 года назад

    Спасибо большое!

  • @Chron4ik
    @Chron4ik 3 года назад

    Шикарный видос!

  • @axelrod4840
    @axelrod4840 4 года назад +33

    Евгений, спасибо за видео урок. Как можно реализовать refresh token на базе этого rest api?? буду очень благодарен

    • @johnmarrewood
      @johnmarrewood Год назад +1

      Похоже ответа не будет(

  • @alexsnowden7077
    @alexsnowden7077 5 лет назад

    Евгений спасибо большое за ваши видео. В них всегда можно чему то научиться. Такой вопрос: у вас есть опыт работы с чем то реактивным spring web flux или просто rxjava? Было бы супер увидеть от вас видео на такую тему.

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +1

      Спасибо за отзыв, Alex.Да, конечно. Я подумаю, что можно сделать на эту тему.

  • @sergios7777
    @sergios7777 2 года назад

    Все хорошо, конечно, слов нет, большое спасибо! Но было бы совсем замечательно, если бы Евгений каждый момент, который он называет "ну, тут все понятно", хотя бы в двух словах пояснял. Потому что, если я пришел смотреть такое длинное видео, значит я просто не знаю, как в принципе реализовать Security на Spring с JWT. Приходится гуглить все не пояснённые моменты типа что такое grantedauthority и simplegrantedauthority и еще несколько вещей. А тем кому все эти моменты понятны, он точно не будет тратить 1,5 часа на видео. Я ничего против сказать не хочу, абсолютно весь материл Евгения просто бестселлер, браво! Поэтому когда я искал в youtube как реализовать Spring Security with JWT и увидел Евгения, с радостью начал смотреть, так как уже знаком с высоким профессионализмом этого замечательного человека и программиста, а данный комментарий -- просто рекомендация.

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 года назад +1

      Спасибо за отзыв, Сергей! На канале есть более подробное видео по основам Spring Security:
      ruclips.net/video/7uxROJ1nduk/видео.html
      Надеюсь, оно будет более полезно. И спасибо за рекомендацию.

    • @sergios7777
      @sergios7777 2 года назад

      @@EugeneSuleimanov Огромное спасибо, завтра займусь изучением!

  • @antontevs
    @antontevs 3 года назад +3

    Субъективно, Flyway по удобней будет для миграции, и понятный sql, а не xml.

  • @brsbrs9275
    @brsbrs9275 3 года назад +1

    Евгений, спасибо за урок!
    Вопрос: если мы маппим юзеров и роли через @ManyToMany, то в итоге получаем три таблички (users, roles, users_roles), однако, если мы сделаем @ManyToOne, то у нас будет только две таблицы (users, user_role(Id, user_id, role) и нужно указать уникальную связку user_id+role).
    Есть ли между этими подходами принципиальная разница и, если есть, то какой лучше?

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад

      В данном случае - нет, но, тогда мы не сможем присвоить юзеру несколько ролей. Это, не критично, потому что мы можем дать каждой роли определённые права (добавить коллекцию пермишенов, например).
      Сказать, какой лучше сложно, но, я бы использовать @ManyToOne в реальной жизни.

  • @FoxChpoks
    @FoxChpoks 2 года назад

    При отсутствии в заголовке токена необходимо возвращать 401 Unauthorized, а не 403 Forbidden. В данной имплементации, к сожалению, всегда 403.

  • @user-wq5nx4lr5l
    @user-wq5nx4lr5l 4 года назад +1

    Спасибо большое! Сделайте, пожалуйста, курс по микросервисам, докеру)

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +8

      Это соль на рану. Уже крайне давно хочу записать серию подобных видео, но не хватает времени, к сожалению... ((
      Крайне надеюсь, что в этом году получится. Спасибо.

    • @user-wq5nx4lr5l
      @user-wq5nx4lr5l 4 года назад

      @@EugeneSuleimanov Удачи!

  • @valeralemberov2853
    @valeralemberov2853 Год назад +1

    Очень круто. Ты красава!!!!

  • @user-gc8py3zp3q
    @user-gc8py3zp3q 4 года назад +2

    Добрый день! Как отправить POST запрос для создания нового пользователя и сохранения его в базе, если еще никакой пользователь не авторизован?

  • @Igorch81
    @Igorch81 2 года назад

    Спасибо за отличный урок!
    Просьба разъяснить насколько это хороший тон или насколько это актуальный подход, что при каждом REST запросе идет обращение в базу для получения данных пользователя. Разве не требуется поместить эти данные в claims?
    Спасибо!

  • @sear5126
    @sear5126 Год назад +1

    Рекомендую вам в дальнейшем использовать Lombok, удобная тула для того чтобы избавиться от явной прописи конструкторов геттеров и сеттеров (если вкратце)

  • @petrovandrey9735
    @petrovandrey9735 Год назад

    Евгений, спасибо огромное за Ваш труд! Спустя годы контент все равно остается полезным и актуальным!
    Проясните пожалуйста несколько вопросов по видео:
    1) 38:58 А зачем все-таки этому классу пустой конструктор по умолчанию? И можно здесь же пояснить зачем именно сделали класс final (т.е. в чем будет проблема если он будет не final)?
    2) 42:15 почему User.roles нужно копировать в new ArrayList перед конвертацией в GrantedAuthorities (строка 26)?
    3) 57:12 Зачем переопределять метод authenticationManagerBean ничего не меняя по сути в имплементации, а только вызывая super (т.е. он и без этого будет создан спрингом и вести будет себя точно так же)?

    • @engend3405
      @engend3405 Год назад +3

      Привет.
      1) Видео сделано давно , есть неточности и чтобы их поправить , придется повозиться.
      2) По пункту 1, 2 согласен.
      3) Возможно, при создании своей аутентификации , ты обязан явно внедрить бин
      AuthenticationManager в AuthenticationRestControllerV1, поэтому в конфигурационном классе его как раз и создаешь (без всяких super. и само собой не переопределяя , хотя раньше нужно было).
      * В целом крутой материал , как всегда - спасибо)

    • @lehatalant9878
      @lehatalant9878 Год назад

      Прошло уже какое-то время, но могу ответить на второй пункт. Копирование делается на тот случай, если мы роли храним не в List, а в Set. Недавно столкнулся с этим пока практиковался и теперь запомню на долго)

    • @petrovandrey9735
      @petrovandrey9735 Год назад

      @@lehatalant9878 поясни плиз в чем именно может быть проблема даже если где-то Set будет?

    • @lehatalant9878
      @lehatalant9878 Год назад +1

      @@petrovandrey9735, Spring Security работает только с List и если мы вставим Set, тогда появится ошибка.

    • @petrovandrey9735
      @petrovandrey9735 Год назад +1

      Понял, спасибо!

  • @user-js4px6fe1p
    @user-js4px6fe1p 4 года назад

    спасибо!

  • @user-uq4nd6go9c
    @user-uq4nd6go9c 4 года назад +1

    Круто.

  • @hrachkhachatryan3006
    @hrachkhachatryan3006 4 года назад

    zdrastvuy uvajaemi Eugene , spasibo bolshoe chto delishsya svoim opitom i znaniem s nami) prosto malenkaya prosba k tebe, font size nemnojko uvelichivay

  • @qweezique
    @qweezique Год назад

    Большое спасибо!

  • @yaqubaliyev8245
    @yaqubaliyev8245 Год назад

    "toor" Молодец Женя😁

  • @budha_rain
    @budha_rain Год назад

    Спасибо

  • @kassymbekoff
    @kassymbekoff 3 года назад

    Спасибо за урок! Когда истекает срок jwt token-а spring возвращает html страницу, а надо ведь json, пробовал ControllerAdvice, результата нет, пока ищу ответ. Как можно это решить?

  • @sergiisavin5633
    @sergiisavin5633 5 лет назад +1

    very good tutorial!

  • @user-oj5gu2kb1k
    @user-oj5gu2kb1k 4 года назад +5

    Как это все сделать с решрештокеном?

  • @tobikron
    @tobikron Год назад

    32:35 Хорошо)
    мой перфекционист в душе: ну что же тут хорошего, где передаваемый параметр? 😁

  • @timyp64
    @timyp64 2 года назад

    А как можно совместить авторизацию с вводом пароля и логина + csrf токен(стандартную для спринг секьюрити) и jwt для подальшего использования (после авторизации) при отправке запросов на сервер? Подскажите.

  • @Alex-qy9zm
    @Alex-qy9zm 4 года назад

    Четко!

  • @volandio
    @volandio 5 лет назад

    Лайк ещё до просмотра)

  • @Ackois
    @Ackois 2 года назад

    Добрый день! Проясните, пожалуйста, такой момент.
    В методе контроллера login вызывается
    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, requestDto.getPassword()));
    При этом, насколько я понял, используется дефолтный AuthenticationManagerDelegator в качестве реализации.
    Если так, то в каким образом он понимает, как валидировать пароль при логине? То есть как связаны конкретно ваша таблица с паролями и сущность authenticationManager?

  • @nikolaykalmykov9025
    @nikolaykalmykov9025 3 года назад

    Добрый день. Отличная работа, очень благодарен. Но возникает один вопрос. Как данный сервис использовать между всеми докеризированными микросервисами? Не создавать же отдельный jwt server для каждого. Должно быть решение.

  • @adlhbgreqk
    @adlhbgreqk 3 года назад

    А почему классам User и Role нельзя сразу имплементировать интерфейсы UserDetails и GrantedAuthority соответственно?

  • @Alexander-ji4mt
    @Alexander-ji4mt 5 лет назад

    Здравствуйте, очень хочется узнать Ваше мнение по поводу построение большого REST приложения по правилам хорошего тона.
    Есть ли смысл и правильно ли использовать использовать entitiy класы и для бизнес логики и для представления, или же последнее заменять так званными проекциями в spring фреймворке? Как я понимаю entity классы отвечают за представление таблицы базы данных, и использовать эти классы в качестве представление не есть хорошо. Хотелось бы узнать это от более опытного разработчика.

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +1

      Добрый день, Александр. Если делаем с нуля, то я бы использовал DTO или graphQL. Есть некоторые ситуации, когда передача самих объектов может подвести. Это, если в общих чертах.

    • @Alexander-ji4mt
      @Alexander-ji4mt 5 лет назад

      @@EugeneSuleimanov Спасибо за ответ!

  • @alexandrugrubneac4199
    @alexandrugrubneac4199 3 года назад

    @Eugene Suleimanov: Благодаря Вашим видео учусь писать правильно код. Спасибо за это огромное. Вопрос: В чем смысл аннотации @Table @Column если имя совпадает с названием таблицы/столбца ?

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад +1

      Это не обязательно, но, обычно, именуют, просто как договоренность. Это на основании моего опыта. Спасибо за отзыв!

  • @user-qr6nm9mj2s
    @user-qr6nm9mj2s 11 месяцев назад

    Спасибо за видео, очень наглядно и полезно. Возник такой вопрос: как можно перехватить JwtAuthenticationException, чтоб на условный фронт возвращался не код 500, а можно было вернуть 401 или 403?

    • @EugeneSuleimanov
      @EugeneSuleimanov  11 месяцев назад

      Спасибо за отзыв!
      Через глобальный обработчик ошибок.

  • @firewatermoonsun
    @firewatermoonsun 2 года назад

    1:10:15 При вводе неправильного пароля "throw new BadCredentialsException("Invalid username or password");" кажется не сработал? Не вижу этого сообщения в json response.

  • @roebinin
    @roebinin Год назад +2

    @50:14 Очень понравился материал. Однако, не разумнее затолкать инициализацию бина passwordEncoder в корень конфигурации проекта, а не в отдельном (пусть и магистральном) компоненте JwtTokenProvider?

    • @ALexus6192
      @ALexus6192 Год назад

      У меня вообще ошибка выпадает, что не может найти такой бин

  • @VDHCUR
    @VDHCUR 3 года назад +1

    Спасибо за видео!
    Делаю по вашему примеру и возникла проблема: при отправке невалидного токена JwtAuthenticationException возвращает HTML вместо JSON. AuthenticationEntryPoint перехватывает эту ошибку, только в случае пустого токена, при каких либо данных в токене возвращается HTML.
    Подскажите, как это можно обработать, а то уже который день над этим бьюсь) Заранее спасибо :)

    • @kassymbekoff
      @kassymbekoff 3 года назад

      Разобрались?) Я тоже на этом застрял))

  • @AXE0707
    @AXE0707 3 года назад +1

    Евгений спасибо большое за видео. На текущий момент это единственное видео, которое мне помогло добавить авторизацию с jwt. У меня есть небольшой пет проект с Spring Boot и Angular, где я успешно внедрил данную технологию, но так же у меня остались вопросы в частности о том как создавать refresh токен и как с ним работать. Но больше всего мне интересно как работать с Oauth2 и как совместить его с jwt. Как бы я не старался, у меня не получается совместить их вместе из-за очевидной нехватки знаний и опыта в этих технологиях. Как сделать так, что при запросе с сервера Angular по REST пришел запрос на сервер Spring и Spring обратился к внешнему серверу(Facebook, Google etc, на которых у меня заранее установлено приложение и известны все данные в виде секретного ключа и так далее) и получил от них ответ в виде данных пользователя, который потом передаст по REST другому серверу(Angular). Такая же схема работы с jwt и по сути они отличаются только тем, что от внешнего сервера(Angular) в случае авторизации через Oauth2 запрос приходит с пустым телом(т.к. нет ни пароля ни логина, они вводятся на стороннем 3м сервере). В конченом итоге я хочу реализовать авторизацию пользователя на выбор: или через внутреннюю самого сайта, или через внешнюю(Facebook, Google etc). Все эти сведения только лишь теоретические. Как это реализовать? Куда смотреть-хотя бы на каких ресурсах внятно это объясняется или я не так понимаю, ибо чем больше я ищу информации про REST авторизацию с Oauth2, тем больше у меня возникает вопросов. В идеале хотелось бы увидеть Ваше видеоруководство на подобии текущего, где будет освещенная данная проблема, т.к. я считаю данный вопрос не является каким то частным случаем, на каждом современном веб-ресурсе присутствует авторизация двумя вышеперечисленными способами. Даже на украинском гос.сайте по оплате ЖКХ услуг есть авторизация через гитхаб.

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад +1

      Спасибо за ваш отзыв!
      Что касается OAuth, то мне кажется, здесь важно понять сам принцип этого подхода (кто именно проверяет безопасность в данном случае).
      Пока в планах нет выкладывать видео по данной теме, но, мне кажется, что уже есть готовые примеры

    • @Multi111111112
      @Multi111111112 Год назад

      100% уже неактуально для автора вопроса, но для ищущих информацию может быть полезно. Шестое издание "Spring в действии" помогает в реализации по данной тематике. Глава 5.3.3.

  • @4val0v
    @4val0v 5 лет назад

    Еще хотелось бы узнать ответ на такие вопросы:
    В BaseEntity.java класс должен быть abstract ? (У вас просто public class BaseEntity)

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +3

      Не обязательно, но можем сделать и абстрактным. Здесь нет методов, которые были бы нам нужны абстрактными, только общие поля. Поэтому - обычный класс

  • @user-oj5gu2kb1k
    @user-oj5gu2kb1k 4 года назад

    Что-бы юзеру фабрику не городить можно в userDetailIml просто положить полем appUser и с него уже выдать все поля геттерами

  • @aoveden197727
    @aoveden197727 3 года назад

    Евгений, уроки отличные. Но надо сделать хороший звук (здесь он глухой и не заднем плане где-то) и картинку с кодом крупнее.

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад +1

      Спасибо, да,есть проблем, стараюсь исправить в новых видео.

    • @aoveden197727
      @aoveden197727 3 года назад

      @@EugeneSuleimanov где можно вам задать вопросы по этому проекту? На первом же запуске, когда ещё БД пустая, выдаёт ошибку. Дальше писать не рабочее приложение не интересно. Есть какой-нибудь оперативный вариант связи с вами?

  • @user-ni2kv6lj8r
    @user-ni2kv6lj8r 4 года назад

    Подскажите пожалуйста, столкнулся с проблемой, делал реализацию jwt в спринге по полному подобию, всегда получаю 403 статус, то есть токен я получил, запихнул в headers, но получаю Access.DENIED. просто написал тоже на спринг бут и все работает, может для простого спринга нужны какие нибудь дополнительные конфиги? Буду благодарен за любой совет

    • @shunic
      @shunic 4 года назад

      Привет, тоже только что запарился с этим. Весь прикол в том, что в станлартном виде мы отсылаем "Bearer " + наш токен, а автор видео отсылает "Bearer_" + наш токен. Потому, когда ресолвится токен, система его не воспринимает

    • @user-ni2kv6lj8r
      @user-ni2kv6lj8r 4 года назад

      @@shunic да это самое первое что поменял) я перелопатил все) уже пятый день дебажу, лезу вглубь, чтобы выяснить из за voterы голосуют за отказ

  • @markifive
    @markifive Год назад +1

    Евгений спасибо за ролик по нём написал свой первый jwt для своего проекта. Но мне не совсем ясно зачем админу давали роль и админа и юзера ? Для ендпоинта админа дали роль админа для всех остальных запросов у нас authenticated же есть.

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад +1

      Спасибо за отзыв!
      Даётся набор ролей для гибкости, так как здесь не используются пермишены. В нормальной практике - будет одна роль с набором доступов.

    • @markifive
      @markifive Год назад

      @@EugeneSuleimanov но если бы мы админу не дали права USER он бы смог всё равно пользоваться всеми запросами согласно нашей конфигурации. Верно ?

  • @Shikarno_3000
    @Shikarno_3000 10 месяцев назад

    Добрый день, Евгений, все больше углубляюсь в понимание Spring Security, и почитав много документации и разобрав Ваш пример, у меня возник вопрос:
    Исхожу из посыла, что токен придуман для общения между серверами, чем между пользователями и сервером, чтобы постоянно не брать аутентификацию из сесии ,а там из контекста. и как я понял фронт может обойтись без файлов куки.
    Тем самым токен позволяет не использовать сессии и как я понял использовать аутентификацию только 1 раз если у пользователя нету токена, чтобы его выдать, а в дальнейшем только проверять токен и тем самым ускорять работу.
    Само собой, если кто-то украдет токен, и без лишней проверки на аутентификацию, то будет серверу плохо.
    Но все же,
    1. Вопрос.
    Почему в классе JwtTokenFilter в методе doFilter вы проверяете токен , и если с токеном все хорошо, вы создаете аутентификацию, опять проверяя существует ли юзер в базе данных? но прибегая к обычному UserDetails , если вы используете до этого JwtUser который имплементирует userdetails.Возможно в JwtTokenProvider должен был инжектиться не UserDetailsService, а JwtUserDetailsService?
    2. Вопрос
    Вместо проверки Аутентификации в сесии, мы в том же методете ее устанавливаем каждый раз после проверки токена. Зачем мы это делаем если с токеном все впорядке? почему мы не может пропустить данный пункт(если это еще один пособ проверки, то вопрос исчерпан), и там же мы проводим проверку на null, как аутентификация может быть null, если валидный токен к нам пришел ?
    3. Вопрос
    В контольлере , /login Вы проводите аутентификацию с помощью AuthenficationManager, и если она проходит, то соответственно Вы выдаете токен. Насколько я понял аутентификация происходит автоматически, в остальных методах вы проверку не проводите в ручную, а так же вы не проводите проверку токена, это мне тоже совсем не понятно. Я понял что вы добавили новый фильтр перед аентентификацией, но как его пройти, если токена нету ?
    Фильтр первый увидел токена нету, включает следующий UsernamePasswordAuthenticationFilter ?
    Меня не покидает ощущение что нужно было заоверайдить AuthMeneger на новый провайдер, который связан с JwtTokenProvider и JwtUserDetailsService, или нельзя базовую аентентификацию переписывать на JWTToken? поясните этот момент, пожалуйста.
    Извините, если плохо изложил свои мысли, но данные моменты не дают покоя моей голове, если я что-то не правильно понял о процессе security, разъясните пожалуйста, или дайте ссылку на понимания данного момента.

    • @EugeneSuleimanov
      @EugeneSuleimanov  10 месяцев назад

      Спасибо за комментарий!
      1. JwtUserDetailsService является реализацией UserDetailsService.
      2. В целом, мы могли бы пропустить этот этап. Но иногда мы делаем это, для обработки случаев блокировки юзера. Т.е. есть токен на месяц. Человека заблокировали в БД, но токен еще валиден. И человек сможет иметь доступ к системе.
      3. Login выдает токен, в остальных местах, мы его проверяем в рамках аутентификации.
      Надеюсь, я верно понял ваши вопросы и смог на них ответить.

    • @Shikarno_3000
      @Shikarno_3000 10 месяцев назад

      @@EugeneSuleimanov Большое спасибо за уделенное мне время, теперь все устаканилось в голове.

  • @User-pg2os
    @User-pg2os Год назад

    Добрый день! Проясните, пожалуйста, такой момент.
    В методе контроллера login вызывается
    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, requestDto.getPassword()));
    Какая реализация используеться для authenticationManager?
    Каким образом он понимает, как валидировать пароль и логин?
    Спасибо заранее!

  • @rasZam
    @rasZam 2 года назад

    Правильно ли будет сохранять jwt в БД?

  • @AquaChanneOne
    @AquaChanneOne 4 года назад +2

    Спасибо огромное за видео!
    Возник вопрос: когда пишу в AuthenticationRestControllerV1 аннотацию Autowired у конструктора, то authenticationManager подсвечивается красным и пишет: No beans of "AuthenticationManager" type not found. В чём может быть причина?

    • @AquaChanneOne
      @AquaChanneOne 4 года назад +3

      Если у кого-то была такая ошибка, то она возникает по причине того, что в SecurityConfig у authenticationManager() нужно добавить @Bean

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +1

      Спасибо за отзыв.
      Если работает корректно, то вероятнее всего, проблемы в настройках idea. Если нет - нужно поискать ошибку в коде. Особенно, аннотации.

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +1

      @@AquaChanneOne только сейчас увидел ответ

    • @AquaChanneOne
      @AquaChanneOne 4 года назад +3

      @@EugeneSuleimanov Можно ещё вопрос спросить:
      Открыл ваш проект, ничего не менял, в нём ошибка возникает в классе JwtTokenProvider у поля userDetailsService:
      Could not autowire. There is more than one bean of 'UserDetailsService' type.
      Beans: inMemoryUserDetailsManager (UserDetailsServiceAutoConfiguration. class)
      С чем это может быть связано? Также и у меня в моём проекте, который я создавал параллельно вашему видео, такая же ошибка.

    • @user-ux2lw7oh5z
      @user-ux2lw7oh5z 4 года назад

      @@AquaChanneOneОшибка с authenticationManager, в моем случае, возникла, потому что забыл на класс SecurityConfig повесить аннотацию @Configuration

  • @user-vr9nr9dy1l
    @user-vr9nr9dy1l 4 года назад

    Здравствуйте, Евгений. Возможо, где то просмотрел, но не могу найти связть между hasRole("Admin") и записью ROLE_ADMIN, которую мы добавили в базу. Как происходит проверка на админа, как спринг узнает, что этот пользователь админ? Спасибо.

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад

      Добрый вечер, Данил. Это "вшитая" логика spring security

  • @alexpodshivalov1010
    @alexpodshivalov1010 3 года назад +1

    Очень долго пытался разбраться почему не работает jwtTokenProvider так и не смог. Постоянно получаю различные исключения при разных ситуациях. Так и не могу ни как заставить его работать

  • @Shikarno_3000
    @Shikarno_3000 10 месяцев назад

    Ролики супер, огромное спасибо, но запускайте свои проекты пожалуйста перед Push'ем на гит , а то со скаченного проекта с гитхаба выходит карусель при создании бинов, я конечно ее решил, и тем самым понял ваш урок еще лучше, может быть вы так и планировали ?)))

    • @EugeneSuleimanov
      @EugeneSuleimanov  10 месяцев назад

      Заливал довольно давно и на тех версиях и в той конфигурации, насколько я помню, это работало. Если нет, то прошу прощения за неудобства.
      P.S.: если там ошибка, то это мой фол, а не хитрый план :)

  • @user-hq6nm2tf6j
    @user-hq6nm2tf6j 4 года назад +2

    если уже используешь ломбок, то можно юзать аннотации @RequiredArgsConstructor, чтобы не писать конструктор

  • @GoPetr
    @GoPetr 5 лет назад +2

    Спасибо Вам! В ру сегменте очень не хватает качественных уроков!
    Будет ли ещё видео, так как от Вас год ждал видео))

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад

      Спасибо за отзыв, Александр ) Постараюсь выкладывать чаще. Сейчас работаю над серией видео по Java 11. Но, основная работа занимает много времени. Постараюсь к середине лета добить Java 11 и выложить.

    • @GoPetr
      @GoPetr 5 лет назад +3

      @@EugeneSuleimanov лучше spring. Все же большинство людей смотрят, что бы устроиться на работу. Посмотрите на сегмент хибернейта, спринга. Его практически нет. Мало кто хороше рассказывает. И критически мало видео где создают свое веб приложение. Я знаю только ОДНОГО автора который создаёт свое приложение, но все же не совсем для новичков. Не разжёвывает. Вот у вас выходит ещё. Но и все.

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад +4

      @@GoPetr как вариант. Spring 5 есть заготовки, возможно, переключусь на этот цикл видео. Спасибо за совет

    • @GoPetr
      @GoPetr 5 лет назад

      @@EugeneSuleimanov было бы круто! Спасибо!

    • @4val0v
      @4val0v 5 лет назад +4

      @@EugeneSuleimanov Также за Spring Boot, по Java 11 другие блогеры расскажут, а вот второго Евгения нету на RUclips который также круто будет по spring учить ;)

  • @vladimirpopov8790
    @vladimirpopov8790 3 года назад

    Благодарю за видео! разбираю сейчас данную тему. не могу понять в каком месте spring security сверяется с базой данных? у меня BadCredentialsException на строчке authenticationManager.authenticate.. база данных postge

  • @Flawden
    @Flawden Год назад

    Офигенно

  • @xz8928
    @xz8928 Год назад +1

    А зачем вручную создавать таблицы,если jpa автоматически создаст их?

  • @annakhuseinova8162
    @annakhuseinova8162 4 года назад

    Евгений, здравствуйте! Урок хороший, но мне интересно, существует ли возможность создать фичу "remember me" в связке Spring + JWT?

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +1

      Добрый день, Анна. Вы имеете в виду - "не выходить из приложения" или сохранить пароль и логин на клиенте?

    • @annakhuseinova8162
      @annakhuseinova8162 4 года назад

      @@EugeneSuleimanov имеется в виду, что авторизованный в режиме "remember me" пользователь может закрыть браузер, а через неделю открыть приложение, и ему не нужно будет авторизовываться, он будет иметь доступ к защищенным ресурсам без введения логина и пароля. Мне кажется, фичу "remember me" можно сделать следующим образом: ставим jwt expiration time на 30 дней на бекенде. На фронтенде пользователь, если он включил режим remember me, то ему в ответ прилетит jwt-токен, который будет сохранен в постоянном хранилище браузера (local storage). И будет использоваться 30 дней, пока не истечет. И при logout'е просто удаляем из постоянного хранилища токен. Если пользователь не будет использовать режим remember me, то сохраняем токен только в сессионном хранилище (session storage), и тогда при закрытии вкладки токен исчезнет. Насколько хорош такой вариант?P.S. в ангуляре вышеперечисленные действия очень легко осуществляются, вопрос - безопасно ли это и в целом, насколько хороша такая практика?

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +1

      @@annakhuseinova8162 в этом плане - да, это возможно реализовать. С точки зрения безопасности - не думаю, что это проблема.

    • @annakhuseinova8162
      @annakhuseinova8162 4 года назад

      @@EugeneSuleimanov спасибо за Ваш ответ) И спасибо за уроки :)

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад

      @@annakhuseinova8162 был рад помочь :) Спасибо за отзыв

  • @aojona
    @aojona 9 месяцев назад

    В данной реализации при каждом обращении пользователя фильтр JwtTokenFilter через jwtTokenProvider обращается к базе данных, и тем самым, сводит преимущества JWT токенов на нет. Чтобы это исправить, нужно переписать метод getAuthentication так, чтобы он собирал объект Authentication на основе тех данных, которые есть в самом токене (Claims). Поправьте меня, если ошибаюсь. А так большое спасибо за Ваш видео урок!

    • @EugeneSuleimanov
      @EugeneSuleimanov  9 месяцев назад +1

      Спасибо за комментарий!
      Да, вы правы, мы могли бы сделать и так. Здесь единственное преимущество в том, что изменение статуса мгновенно отразится на аутентификации.

  • @eugenevolohonsky1469
    @eugenevolohonsky1469 Год назад +1

    Попробовал сделать такую же авторизацию на тестовом проекте и столкнулся с тем, что отсутствует WebSecurityConfigurerAdapter. В последних версиях Spring Security его нет. Целый день пытаюсь адаптировать пример из видео под использование без адаптера. Будет ли подобное видео с новой версией секьюрити или разбор что изменилось и как теперь с этим работать?

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад +1

      @Configuration
      @EnableWebSecurity
      public class SecurityConfig {
      @Autowired
      private JwtAuthenticationFilter jwtAuthenticationFilter;
      @Autowired
      private CustomUserDetailsService customUserDetailsService;
      @Bean
      public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
      }
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
      }
      @Bean
      public JwtAuthenticationFilter jwtAuthenticationFilter() {
      return new JwtAuthenticationFilter();
      }
      @Bean
      public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
      http.csrf().disable()
      .authorizeRequests()
      .antMatchers("/api/auth/**").permitAll()
      .antMatchers("/api/**").authenticated()
      .and()
      .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
      return http.build();
      }
      }
      Это примерно. Т.е. используются аннотации и SecurityFilterChain. Это должно помочь.

    • @eugenevolohonsky1469
      @eugenevolohonsky1469 Год назад +1

      @@EugeneSuleimanov спасибо!

  • @user-jj2eu6yx6r
    @user-jj2eu6yx6r 2 года назад +1

    Спасибо! Но , сложновато , конечно ... долго писали базу и модель , в итоге , что касается security , видимо, автор подустал и пошла спешка

  • @MrCurill
    @MrCurill 4 года назад

    Большое спасибо за урок! Не смог понять только, как сделать так, чтобы при отправке POST /auth/login передавался зашифрованный пароль и сравнивался напрямую с тем, что лежит в базе.

    • @user-jd3yo6jx9h
      @user-jd3yo6jx9h 4 года назад

      Никак. Пароль в данном демоуроке не шифруется, а хешируется. Плюс "приготовление" ХЭШа идет с солью. Т.е. один и тот же пароль будет всякий раз давать разный ХЭШ. Но всегда есть возможность имея оригинальный пароль проверить, а от него ли ХЭШ, что здесь и делается. А если вас волнует передача пароля в открытом виде, то вас больше должна волновать передача токена в открытом виде. Эта операция выполняется куда чаще. Но! Взаимодействие клиент-сервер здесь идет по протоколу HTTP. Всего то нужно перейти на HTTPS запросы, что исключит возможность узнать пароль путем анализа трафика. А для этого просто "прикрутите" SSL сертификат к Spring Boot. В Инете полно инфы на эту тему.

  • @DankoBLR
    @DankoBLR Год назад +1

    Добрый день Евгений. Спасибо за качественные ролики. Хорошо что Вы в безопасном месте. Просматривая этот ролик в очередной раз, возник вопрос, на который я так и не смог найти ответ. А вопрос вот в чем. При авторизации пользователя в контроллере в методе "login" мы используем такую строчку: "authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, requestDto.getPassword()));" и в случае если логин и пароль не совпадают, получаем исключение, а именно Spring проводит данное сравнение, а именно как он и где берет данные из БД? Может быть вы подскажите. Спасибо.

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад +1

      Спасибо за отзыв! Это "подкапотная логика спринга". Он сам идет в БД и сверяет соль пароля с солью переданного пароля. Если нет совпадения - кидает исключение.

    • @DankoBLR
      @DankoBLR Год назад

      @@EugeneSuleimanov Спасибо. Еще момент, получается в БД он идет, но пологаясь на UserDetails модель?

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад +1

      @@DankoBLR да, верно

    • @DankoBLR
      @DankoBLR Год назад

      @@EugeneSuleimanov Спасибо за ответы. Если вы не против, хотел бы еще пару вопросов задать. Получается в Security Context лежат действующие токены. Но если реализовать refresh токен, его необходимо хранить в БД. Если его привязать к записе пользователя отдельной ячейкой, мы получим разлогинивание на втором устройстве после того, как протухнит токен доступа. Все же лучше хранить тогда токен обновления в отдельной таблице? Как часто тогда лучше их чекать на протухание и стоит ли их удалять или после протухания просто выстывлять им флаг удаления или фалаг не активности? Спасибо.

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад +1

      @@DankoBLR в данном случае процесс строится несколько иначе, обычно. Здесь неплохое описание:
      www.codeusingjava.com/jwt/jwt4

  • @Nucl3aRxxx
    @Nucl3aRxxx 4 года назад

    Спасибо за видос, было полезно. У меня возник вопрос: почему когда неправильный пароль то в ответе возвращается 403 Forbidden а не 401? Как в этом случае вернуть какую то осмысленную ошибка а не "Access Denied"

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад

      Спасибо за отзыв :). Касательно вашего вопроса - эту ситуацию можно обработать в контроллере и возвращать нужный ответ (AuthenticationRestControllerV1). Метод login в блоке catch.

    • @Nucl3aRxxx
      @Nucl3aRxxx 4 года назад +1

      Eugene Suleimanov а будет видос на тему рефреш токена?

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад +4

      @@Nucl3aRxxx сейчас хочу снять ещё одно видео по JWT с более подробным объяснением и обновлением токена. Но, к сожалению, из-за работы времени остаётся крайне мало :(

  • @ErikGhukasyan
    @ErikGhukasyan 4 года назад

    А что такое Claimes ?

  • @user-bh5go5ci5i
    @user-bh5go5ci5i Год назад

    Здравствуйте, Евгений! Может подскажите как перехватывать исключения внутри пакета jwt.
    ControllerAdvice - перехватывает только все то находится на уровне контроллеров =(. А вот фильтрация происходит раньше, и вот как перехватить исключения ума не приложу
    Может сталкивались с этим?

    • @EugeneSuleimanov
      @EugeneSuleimanov  Год назад

      Добрый день!
      Если вы знаете конкретное
      исключение, то просто через глобальный обработчик можно сделать.
      reflectoring.io/spring-boot-exception-handling/

  • @lance9533
    @lance9533 10 месяцев назад

    Может на спринг буте 2.1.4 это работает, но я встретился с проблемой циклической зависимости, исправил за счет того что вынес бин passwordEncoder в отдельный конфиг класс

  • @user-wy7mz4xy9q
    @user-wy7mz4xy9q 4 года назад

    вопрос по JPA и liquibase.
    Что если задать в XML только таблицы, а поля оставить на JPA? Все корректно отработает ?

    • @EugeneSuleimanov
      @EugeneSuleimanov  4 года назад

      Не готов ответить со 100% уверенностью. Но, мне кажется, что возникнет ошибка. И сам подход не совсем корректный, с моей точки зрения. Если мы используем средства контроля версионности БД, то лучше использовать её полностью.

    • @user-wy7mz4xy9q
      @user-wy7mz4xy9q 4 года назад +1

      @@EugeneSuleimanov понял, спасибо

    • @user-wy7mz4xy9q
      @user-wy7mz4xy9q 4 года назад

      @@EugeneSuleimanov и если можно, еще один вопрос, уже больше по авторизации.
      Можно ли как то прикрутить эту авторизацию между другими микросервисами, и если да, что стоит взять за основу, oauth2 или как то по другому ?

  • @vladislavchernyak6072
    @vladislavchernyak6072 5 лет назад

    Может быть подскажете, как сделать фильтрацию с динамическим количеством параметров на бэкэнде(restful).

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад

      Думаю, это поможет:
      spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

  • @user-bl1ui9fu3b
    @user-bl1ui9fu3b 5 лет назад

    Вопрос, у меня не работает log.info(""); в чём может быть проблема?и ещё, вы в коде не делаете geters и seters, но они работают, у меня же выдает ошибки если я их не создам, в чём может быть дело? только начал изучать JAVA, не судите строго.

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад

      Добрый день, Алексей. Вероятнее всего, проблема в настройках IDEA. Вам нужно зайти в Settings и найти Ebnable annotation processing. И поставить галочку. Это должно решить проблему. Если не поможет пишите, попробуем другие варианты. P.S.: задавайте вопросы - ничего страшного. Всегда постараюсь помочь.

    • @user-bl1ui9fu3b
      @user-bl1ui9fu3b 5 лет назад +1

      @@EugeneSuleimanov Всё решилось установкой плагина Lombok. вдруг кому поможет) Спасибо за отличный урок!

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад

      @@user-bl1ui9fu3b да, это самый первый этап. Забыл написать про него. Спасибо )

    • @user-ir4sx7fs3r
      @user-ir4sx7fs3r 3 года назад

      ​@@EugeneSuleimanov хорошо нашел этот коммент. Так же ломал голову и не понимал что сделал не так. Потом добавил гетеры и сеттеры, после этого и догнал, что проблема в Lombok

  • @alexandarv.2688
    @alexandarv.2688 5 лет назад +1

    Офигенный видос. Но вот только я немного не понял почему автор пропустил поле password в DTO.

    • @EugeneSuleimanov
      @EugeneSuleimanov  5 лет назад

      Спасибо за отзыв. Обычно пароль стараются не отдавать ни в каком виде. Он и не нужен, кроме как для смены пароля, а этот функционал выносится в отдельную логику

    • @4val0v
      @4val0v 5 лет назад

      @@EugeneSuleimanov DTO хорошо, а можно пример как работать с mapstruct ?
      В рунете ничего нет по mupstruct ;(((

  • @ildgud8957
    @ildgud8957 3 года назад

    Круто ! Спасибо! А как по api файлы сохранять и принимать?

    • @EugeneSuleimanov
      @EugeneSuleimanov  3 года назад

      Спасибо за отзыв!
      По вашему вопросу:
      community.postman.com/t/sending-request-with-file-and-text/7385