Spring Boot Security: добавляем панель администратора и роли пользователей, ограничиваем доступ
HTML-код
- Опубликовано: 29 апр 2018
- Spring Boot Web Application (MVC): Добавляем панель администрирования пользователей и управляем правами доступа пользователей с помощью аннотации hasAuthority из Spring Security.
Код из видео:
github.com/drucoder/sweater/t...
В самом начале нам нужно добавить новую роль в список ролей (enum Role) и страницу, отображающую список пользователей нашего приложения. Для этого создадим новый шаблон для списка пользователей. Используем в нем удобный синтаксис для отображения списков через разделитель.
Синтаксис этой директивы описан тут:
freemarker.apache.org/docs/re...
Далее добавим ссылку на эту страницу с главной страницы приложения main.ftl. Следующим этапом добавляем шаблон для редактирования пользователя, где доступными для редактирования сделаны поля username и roles (доступный в виде списка чекбоксов).
В редакторе пользователя для вывода ролей используем функции seq_contains:
freemarker.apache.org/docs/re...
После этого создаем новый контроллер UserController и прописываем в нем эндпойнты для отображения списка пользователей, формы редактирования пользователя и обработки изменённых данных пользователя.
Теперь для любого авторизованного пользователя доступен список всех пользователей и возможность изменения их имени и списка ролей. Значит следующим логичным шагом будет ограничение прав. После добавления одному из пользователей роли ADMIN добавляем на UserController аннотацию
@PreAuthorize("hasAuthority('ADMIN')")
Которая ограничит доступ к панели администратора и оставит его только для пользователей с ролью ADMIN. Но эта аннотация не будет работать до добавления конфигурационной аннотации
@EnableGlobalMethodSecurity(prePostEnabled = true)
На класс конфигурации WebSecurityConfig
Документация по Freemarker:
freemarker.apache.org/docs/in...
Документация по языку выражений Spring Security, определяющих доступ
docs.spring.io/spring-securit...
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
➡ Твиттер: / letscodedru
➡ Чат в Discord: / discord
➡ Группа Вконтакте: letscodedru
➡ Канал в Telegram: t.me/letsCode_dru
➡ Чат в Telegram: t.me/joinchat/FeiP9xEhqHajfqh...
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
Поддержать проект:
➡ Patreon / letscodedru
➡ Яндекс.Деньги money.yandex.ru/to/4100145167...
➡ PayPal paypal.me/letscodedru
➡ Qiwi qiwi.me/letscode
➡ WebMoney/BitCoin funding.webmoney.ru/d/drucoder
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
Ссылка на плейлист:
• Spring Boot 2
Ссылка на канал: / @letscodedru
Ссылка на Яндекс.Дзен: zen.yandex.ru/media/id/5ac209... - Наука
Мне нечего сказать по делу. Сразу подписался, все огонь! Автор, прошу... Нет заклинаю - НЕ ОСТАНАВЛИВАЙСЯ, ВАЛИ ЕЩЕ! Отдача будет, век воли не видать, главное делай и не закидывай! Всем, я думаю, понятно насколько востребован спринг, но материала как твой я больше не видел. Огонь. +100500 тебе в карму ;)
Я в шоке, не представляю как можно так владеть программированием и понимать, что делаешь. Это круто. Тоже хочу.
Ну ооочень круто!!!! Все понятно,без воды! Влюблена в Ваш стиль подачи информации! Спасибо большое! Продолжайте пожалуйста!
Благодарю за Ваш труд. Очень полезно
Андрей, огромное спасибо за Ваши видео! Это огромная помощь в изучении spring!
Еще раз большое спасибо за уроки. Все очень доступно.
Круто! Спасибо большое, очень интересно и вы очень хорошо все объясняете. Ещё раз спасибо! Без вас бы сложно было разобраться.
Спасибо за эти уроки. Очень полезно.
Самые качественные видео на эту тему, /и в рунете, и на англ/ Крутой
подход, спасибо автору. Следовал видео и получилось сделать интересный
проект, который любопытно "докручивать" самому. Советы автора помогают
не утонуть в деталях и не потерять мотивацию. Спасибо ребятам в
комментах - если вы напоролись на ошибку, решения находил здесь же.
Успехов! Хватит читать комменты, идите кодить)
Прежде всего я бы хотел выразить Вам свою благодарность за столь полезные видео уроки.) У меня есть к Вам просьба, не могли бы Вы сделать небольшой видео урок, о том как добавить сюда поддержку Cookie, что бы на форме логина была кнопка - "Запомнить меня", и при следующем нашем возвращении на сервер, он нас помнил и не требовал делать логин снова. Я думаю это будет не менее полезный и интересный урок.) Ещё раз спасибо за то, что вы делаете!)
Это будет в следующем видео. Тут дело не в Cookie (они и так используются на сервере), тут работа с сессиями на стороне сервера
letsCode отлично, с нетерпением ждём Ваших новых уроков.)
Спасибо! Отличный и лаконичный материал!
Отличный курс!
Спасибо за труды!
Очень круто! Спасибо.
Большое спасибо за видео! Было бы интересно узнать о возможностях прикрутить какой-нибудь более объемный функционал. Например, использование капчи при регистрации нового юзера или уведомление о новых сообщениях на почту. Если это, конечно, реально в рамках курса))
Капчу записал, почта в очереди)
Спасибо автору за труд!!!
Этот комментарий для тех у кого вылетают ошибки при добавлении нового пользователя и код точно такой же как у Андрея. На следующем видео все исправляется и это не вы рукожоп (и не Андрей :))
И да, спасибо за уроки!
Спасибо за видео!
Thank you! Very interesting)
отличный урок. Хорошо что есть гит со всей этой фримаркер-херней и можно не тратить время на это. Однако в основном все ошибки идут оттуда :( Очень наглядно и доходчиво показан вход в секьюрити. Спасибо!
Я смотрела ваши видео 3 часа, исправила только одну ошибку. Но видео сделаны хорошо
Отличная подача материала! Спасибо! Очень интересует авторизация по токену ( X-AUHT-TOKEN или аналог) для REST приложения, можете рассказать об этом?
Добрый день, спасибо за видео, как сделать редирект на основную страницу когда мы открываем страницу /login через ссылку в браузере, если пользователь уже залогинелся (authenticated) ?
Подскажите пожалуйста, все работает. Но!! После перезапуска программы- необходимо логиниться заново. Ранее, сохраляло пользователя залогиненным.
Это в ресурсах? Или в классах java проблему искать.
Отличный урок. И главное, самый нормальный голос!!!
да лааааадна. У меня в этом видео еще аппаратуры нет и микро говна кусок)
@@letsCodeDru не скромничай)))
Как всегда замечательно! Спасибо! Хотелось бы увидеть разграничения прав на страницы с админ панели без хардкода роли в атрибутах, т.е. динамически с админ панели привязывать роли на страницы.
Можно кейс реальный? Потому как данный способ разделения прав покрывает почти все соответствующие потребности. Далее я планирую сделать видео про разграничения прав на основании настроек пользователей, но это совсем про другое. А так не совсем понимаю, что значит "без хардкода роли в атрибутах"
Есть админ, который с гуи решает какой пользователь(роли) может иметь доступ к той или иной странице или может лишить право доступа к той или иной странице. Также может дать право на редактирование. Главное все происходит с гуи.
Ну так я это и решаю в данном видео)))
Процесс: делаем роли, например "просмотр документов" и "редактирование документов" и вешаем на методы аннотации, проверяющие наличие этих ролей у пользователей. На метод "список документов" и "просмотр документов" у нас запрашиваются права на просмотр, а на "редактирование" и "создание" - права на редактирование. В интерфейсе пользователям ставим галочки "может читать" или "может редактировать"
Более того, далее у нас в пределах одной страницы будут проверяться права на разные действия. Роли будут определять что видит пользователь на странице и у админа и рядового пользователя одна и та же страница будет выглядеть по-разному)
Очень хороший канал. Жаль, но на данный момент оказать материальную помощь каналу не имею возможности, Это правда что если переходить по рекламной ссылке, автору будет капать бонус? Хочется хоть как-то поддержать
Подскажите, чем redirect:/user отличается от просто userlist возвращения?
просто бесподобные видео
Такой вопрос - у кого получилось полностью(так что бы работали не только методы get но и post) подключить swagger к данному проекту? поделитесь секретом
Подскажите как будет выглядеть userList в thymeleaf уже нервы на переделе. спасибо заранее за ответ!
как сделать тако что бы кнопка список пользователей отображалась только у админа?
Андрей, поясните, пожалуйста, метод #userSave
В нем параметр (userId) User user передается в случае редактирования существующего метода
А как будет выглядеть сигнатура метода (будет ли в ней (userId) User user) в случае заведения нового пользователя?
Спасибо
Почему, если у корневого обработчика greeting написать аннотацию @GetMapping("/"), то всё работает, а если @GetMapping, то не открывается ни main page, ни login? Автор же вроде говорил, что это одно и то же, разве нет? Разница только в лишнем слэше в конце пути, но почему при @GetMapping вообще ничего не работает?
а @Transactional не надо на userSave вешать? или проект однопоточный?
СПАСИБО!!!!
Объясните, как из формы с чекбоксами получается -> @RequestParam Map form
Возвращаются только пары с значением string("checked")?
Которые string("") не попадают в мапу?
Шикарно
Как у тебя получается засетить цельный объект с PathVariable...
@GetMapping("{user}")
...(PathVariable User user, ...)
вроде в документации написанно что можно только простые типы "A @PathVariable argument can be of any simple type such as int, long, Date, etc. "
Андрей, СПАСИБО ВАМ ЗА ТАКОЙ ПОЛЕЗНЫЙ ВИДЕОКУРС!!!!
У вас самый замечательный канал!! Вы очень хорошо объясняете, чувствую, что по вашим видео я очень быстро росту!! Такой вопрос, а можно сделать как-нибудь, чтобы ссылку перехода для изменения прав пользователей отображалось только для администратора? Возможно я забегаю на перед, но наверно вы уже сделали эту возможность в следующих уроках, так что, пошел смотреть следующий урок)))
Вроде было где-то, про ограничение видимости.
Рад, что мои видео помогают :)
Thanks a lot.
4.20 как сказал автор спринг должен сам найти пользователя в БД и подставить его. Так вот у меня этого не происходит. Просто Failed to convert value of type 'java.lang.String' to required type 'com.example.sweter.domain.User';
По поводу: user.getRoles().add(Role.valueOf(key));
Разве безопасно написан геттер, который выдает закрытое поле в контроллер и там его можно изменять через вызов?: public Set getRoles() { return roles; }
При запуске на пустой базе, когда еще нет ни одного пользователя, не возможно будет получить права ADMIN. Ведь если их ни у кого нет, то не возможно попасть на форму регистрации. Напрашивается вариант, когда первый пользователь получает права админа автоматически. Вопрос, как правильно это реализовать? Если проверять при регистрации наличие пользователей в базе, то это лишнее обращение.
if(userRepo.count()>0) {
user.setRoles(Collections.singleton(Role.USER));
}
else{
user.setRoles(Collections.singleton(Role.ADMIN));
}
Есть ли вариант лучше?
Есть. Скоро покажу) очень много всего надо показать, просто не успеваю генерировать контент.
спасибо огромное
Особое вам спасибо за подробнейшие аннотации к вашим видео.
Ой... вы не могли бы поправить ссылочку на "Документация по языку выражений Spring Security, определяющих доступ"
Спасибо за заметку. Сломали ребята адрессацию. Исправил ссылку
Андрей, буду вам очень признателен, если найдете возможность ответить на последнюю группу моих вопросов к этому видео .. обещаю, последнюю - все же хочется разобраться досконально.
1. объект Model, который передается из метода в метод - он доступен лишь в случае @Controller? Или, поскольку @RestController = @Controller + @ResponseBody, можно что угодно писать/читать из Model и в случае @RestController, передавая его аргументом методов?
2. по поводу карты - аргумента #userSave. Эта карта - перечень всех элементов формы? Её ключами являются их (элементов) атрибуты name ?
3. похоже, что в #userSave мы передаем как все элементы формы (в виде map), так и выборочные из них (кроме чекбоксов) - так проще ?
4. Если в форме будет несколько наборов чекбоксов (в DO несколько вложенных коллекций), все из них мы распарсим как в вашем примере, проблем не возникнет (если не будет совпадений name)?
6. В примере форма редактирует. Но что если нужна возможность сохранения нового пользователя (с созданием экземпляра DO в #userSave) - будет ли тогда два метода (один из которых PUT({user}), или как-то можно приспособить единственный POST-метод? Как тогда отличить ситуацию редактирования от создания?... если воспользоваться PUT, то один и тот же DO играет роль и @PathVariable - при поиске, что обновить, и @RequestParam - при обновлении?
Большое вам спасибо за крайне полезный контент
1. Модель для рест не нужна
2. Да
3. Да
4. Да
5. Да
6. У нас тут не рест, поэтому можно все через пост слать. Для рест приложения мы знаем для каждого экземпляра объекта является ли он новым или нет. Исходя из этого знания мы и используем пост или пут.
Но, по большому счету это всего лишь рекомендации и отступление от них - не нарушение закона, а, всего лишь, потенциальная путаница, остающаяся на совести разработчика. Если все красиво и понятно оформить, то можно и отступить в отдельных случаях
Можно как-то перенаправить пользователя без прав админа на страницу с сообщением о недостаточных правах?
Спасибо за уроки! И сразу вопрос.
Зачем в userEdit.ftl выводится скрытое поле user.id?
И еще. Как все-таки сделать выпадающее меню? У пользователя, по идее должна быть одна роль (имеется ввиду текущий список - или USER или ADMIN)
Решение от подписчика
${role}
не работает. При выборе одной из ролей из списка и нажатии "Save" просто пропадает запись из таблицы 'user_role' в БД
user.id для того чтобы потом понять какого пользователя изменить
в UserController
@RequestParam("userId") User user
по выпадающему меню HTML тебе в помощь:
www.mobila.name/post/5172c6cbb4ab3/
Подскажите, Андрей,
если я правильно понимаю, поведение GetMapping / PostMapping варьируется в зависимости от того, объект какого типа возвращает метод:
Если строка, то она подразумевает имя шаблона, куда осуществляется редирект
?
А если POJO, то он вернется запросом в виде XML/JSON? - Так?
И почему лишь в методе userSave() возникла потребность воспользоваться инструкцией "redirect/"?
Значит ли это, что GET-метод должен возвращать просто String, а POST-метод при необходимости редиректа - redirect/?
Чисто стилистическое отличие?
Мэппинги ничего не решают. Решает аннотация Controller и RestController
Первая означает, что методы возвращают строку, указывающую, какой шаблон отображать. Вторая говорит о том, что все методы этого класса возвращают объекты, которые должны быть сериализованны в json. Из класса с аннотацией Controller также можно возвращать json, но тогда на методы, которые возвращают его, надо вешать аннотацию ResposeBody
Спасибо за качествееный контент! Скажите пожалуйста может есть в планах как сделать авторизацию с REST?
Так уже на канале гугловая авторизация) последние видео как раз о том
Подскажите, только у меня сломалось регистрация и добавление новых сообщений.
Мы же не ставили лимит ни на сообщения, ни на юзеров.
Сделал 30 сообщений, выдает теперь ошибку.
Пытаюсь зарегать нового юзера, тоже выдает ошибку.
ПыСы код аналогичен как и у Андрей.
в предыдущем видео есть варианты решения проблемы, у меня получилось исправить
Так дядька ты крутой 😎 я бы это пол дня делал 😂ты за 15 минут 😂ты мастер джедай в мире джава 😂если не выше 👆
подскажите как вывести на форму текущего пользователя?
Всем привет! Видос классный, но я так и не понял как назначать роль изначально. В видео показано как заходит пользователь у которого уже есть роль. Как мне предоставить роль админа для определенного человека?
Здравствуйте, Андрей, почему-то после перехода на Freemarker с Mustache, отвалилась менюшка logout (стала Forbidden), настрйка viewName через MvcConfig не помогла, подскажите пожалуйста как решить проблему(
Но вообще огромное спасибо за все ваши видео и правда очень помогают)
Не подскажу без кода. Попробуй сравнить свой код с моим. На канале есть видео, как сравнивать код, с ним сильно проще
Все супер, хотелось бы увидеть, а если есть готовый фронт энд, как его правильно надевать на спринг
С болью и слезами. Это же реверс-инжиниринг, тут нужна особая мотивация. Я сценарий не придумаю для такого видео
И еще вопрос, по логике работы с Hibernate. Для сохранения изменений в объекте User (его листа с ролями) мы используем метод .save Но, по описанию, метод .save выполняет сохранение в БД, как нового объекта с выдачей уникального идентификатора, а мы же просто обновляем старый объект. Куда девался тогда старый объект? Может нужно использовать метод update? В чем ошибка моих наблюдений?
Метод save из JpaReposotory делает update если уникальные поля(@id) совпадают.
letsCode, Здравствуйте. Не подскажете, зачем мы в userEdit.ftl добавляем
${user.roles?seq_contains(role)?string("checked", "")
?
Просто я удалил эту строчку и у меня также добавляются роли в любых комбинациях: отсутствие ролей, юзер, админ, юзер+админ.
Это для чего-то вообще нужно?
Помечаем галочкой в итоговой форме те роли, которые уже установлены пользователю
Если у когото возникает подобная ошибка:
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'com.rog.teach.domain.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value '1 057'; nested exception is java.lang.NumberFormatException: For input string: "1 057"
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.rog.teach.domain.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value '1 057'; nested exception is java.lang.NumberFormatException: For input string: "1 057"
то есть ответ тут:
qna.habr.com/q/696518
суть в том что в userList.ftlh надо ставить Редактировать
и в userEdit.ftlh
9:33 никак не получается добавить добавить Role.values() в Arrays.stream(), что за комбинацию клавиш нажимает автор, чтобы аргументы автоматически переносились. Кто знает подскажите.
CTRL+ALT+V
Как делать ajax запросы? куда давать csrf токен, и можно ли это как-то автоматизировать? Заголовки ajaxSetup jquery как вариант например сгодятся?. Его (токен) возможно получить только выдергивая из скрытого инпута в самом начале?
Как примерно работать в спринге с сессией? Куда копать.
Пробовал на thymeleaf авторизоваться/регаться, работает только через дополнительный открытый в конфиге секюрити мапинг реги только на get.
Он почему то не подсовывает токен, хотя должен по умолчанию
При Ajax запросах надо csrf в заголовки запроса кидать. Чтобы на клиенте их удобно получать, можно его либо в js переменные подложить, либо в какой-нибудь тэг засунуть и потом распарсить. Подробнее - скоро на канале будет серия видео про rest spa приложение на спринг.
Отличный курс!
Вопрос:
А как можно удалить юзера? Удалять по id не получается, т.к. там связанные таблицы.
удалить связанные записи сначала
Можно ли как-нибудь получить список пользователей с определённой ролью например только с ролью админ или юзер?
Все ни как не могу понять как это сделать
Попробуй так:
public List getAdmins() {
return userRepository.findAll().stream()
.filter(user -> user.getRoles().contains(Role.ADMIN))
.collect(Collectors.toList());
}
Благодаю за видео! Исходя из видео напрашивается вопрос - как ограничить доступ к чему либо в шаблоне freemarker в зависимости от роли пользователя или статуса авторизации? Например, показать ссылку выход и профиль, если пользователь авторизован, а также показать ссылку Админка если роль пользователя админ. P.S. столкнулся с проблемой использования taglibs
Используй например блок if из frermarket:
...
...
и проверяй какой нибудь дополнительно передаваемый параметр например:
if(user.getRoles().contains(Role.valueOf("ADMIN"))){
model.addAttribute("admin", true);
}
только надо добавить в блок метода get аннотацию в принимаемых параметрах
@AuthenticationPrincipal User user
@@egorbosolyho3287 благодарю за помощь. Я решил чуть другим способом - перешел на thymeleaf, а там как-то попроще) Уже все подключил и все работает. Еще раз спасибо
А можно сделать, чтобы обычному пользователю не отображалась ссылка на user list?
Может быть вы уже знаете ответ? Если да, скажите если не трудно.
В main.ftlh добавил
User list
В MainController
model.addAttribute("isAdmin", user.getRoles().contains(Role.ADMIN));
Подскажите пожалуйста следующее. Делаю все как в видео, но у меня почему-то Spring не хочет собирать объект класса User, который я собираюсь отредактировать.
@GetMapping("{user}")
public String userEditForm(@PathVariable User user, Model model) {
// System.out.println(user);
model.addAttribute("user", user);
model.addAttribute("roles", Role.values());
return "userEdit";
}
Этот метод мне выбрасывает следующее исключение:
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'me.vrnsky.mimiter.domain.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable me.vrnsky.mimiter.domain.User] for value '1'; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Provided id of the wrong type for class me.vrnsky.mimiter.domain.User. Expected: class java.lang.Integer, got class java.lang.Long; nested exception is java.lang.IllegalArgumentException: Provided id of the wrong type for class me.vrnsky.mimiter.domain.User. Expected: class java.lang.Integer, got class java.lang.Long
В котором как я понял он не может сконвертировать тип. Я гуглил решение проблемы, но ничего толкового не нашел.
github.com/vrnsky/mimiter - ссылка на репозиторий. Подскажите пожалуйста в чем может быть проблема
Нашел, причину. Я неправильно объявил поле id у класса User, у меня было integer, а надо было long
Такая фишка. Попробовал в userService сделать такую штуку для сохранения пользователя (его ролей)
user.setRoles(new HashSet(){{
add(Role.USER);
add(Role.ADMIN);
}});
Это для того, чтобы я смог зарегать админа сначала. Потом уберу строку с добавлением роли Admin. Меня удивляет то, что аннотация @PreAuthorize("hasAuthority('ADMIN')") меня все равно не пропускает. Надо как-то по-другому добавлять роли? Просто в вашем примере, в видео, показано, как можно обновить роли или изменить их. Я просто пишу свой проект и хотел бы узнать, как правильно роль добавить. Я не предполагаю изменение ролей (которые кстати почему-то дико сложно обновляются, судя по вашему примеру).
Кстати, не пропускает, если я даже добавлю через синглтон, как у вас, только роль ADMIN.
implements UserDetails в классе User. Там нужно реализовать метод getAuthorities
Приветствую. Когда выводится ошибка 403, то после текста ошибки снизу идет куча всякого текста с ошибками от страницы(наш проект). Я так понимаю, спринг все же дает ссылку на страницу userList, но без прав доступа она не может вывести инфу и получается белидерда. Кто знает в чем дело? Как редиректом вывести заранее заготовленную страницу на ошибку 403?
Tue Sep 29 14:42:28 MSK 2020
There was an unexpected error (type=Forbidden, status=403).
Forbidden
org.springframework.security.access.AccessDeniedException: ?????? ????????
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.example.sweater.controller.UserController$$EnhancerBySpringCGLIB$$7fb063b5.userList()
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Met
Я правильно понимаю, что мы конфиг для доступа к эндпоинтам настраиваем либо через аннотации к контролерам, либо настройкой configure через antMatchers и hasAuthority в классе, унаследованном от WebSecurityConfigurerAdapter, совмещение двух способов невозможно? И почему так происходит, пробовал совместить, не получается.
Предпочтительнее я так понимаю разграничение доступа на уровне аннотаций контроллеров? Или есть подводные камни?
developer.okta.com/blog/2019/06/20/spring-preauthorize
Здесь описаны отличия двух методов.
Пытаюсь запустить код автора, но при попытке добавить нового пользователя выскакивают ошибки:
Add new user FreeMarker template error (DEBUG mode; use RETHROW in production!): The following has evaluated to null or missing: ==> message [in template "registration.ftl" at line 6, column 3]
Из-за чего может такое быть?
Убрал из registration.ftl строку с ${message}, заработало
Правильнее будет сделать ${message!}, если месседжа нет, то и ошибки не будет, но и сообщение том, что такой же юзер уже есть в БД, будет выводиться.
Та же проблема
хочу сказать что по этому коду, если не вводить ни логин ,ни пароль и просто нажать sign in то войдет без пользователя! как решить эту проблему?
у тебя сохранился пользователь со значениями логина и пароля null null - проверь
Egor Bosolyho а как запретить ввод null спасибо!
@@Imkarabl1 добавить проверку на пустоту при регистрации user.getUsername().isEmpty()
У меня при добавлении в enum Role значения ADMIN не обновляется enum в таблице user_role, соответственно после добавления роли и перенаправления на user вылетает 500. Вручную добавил в таблицу значение 'ADMIN" - заработало. С чем это связано? Потому что Role не Entity? Или еще что то?
Потому что у таблицы стоит ограничение по значению. Она принимает только USER, так как когда она создавалась автоматически, то других ролей не было. Я сделал как написали в комментах перед тобой. В консоле базы данных ввёл:
ALTER TABLE user_role DROP CONSTRAINT user_role_roles_check;
ALTER TABLE user_role ADD CONSTRAINT user_role_roles_check CHECK (roles::text = 'USER'::text OR roles::text = 'ADMIN'::text);
Здравствуйте! Делаю один в один, как у вас. Но при попытке нажать "edit" получаю одну и ту же ошибку There was an unexpected error (type=Internal Server Error, status=500).
Failed to convert value of type 'java.lang.String' to required type 'com.example.postgresqltest.domain.User'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.postgresqltest.domain.User': no matching editors or conversion strategy found.
Многое перепробовал - не помогает. Что можно сделать? Заранее,спасибо.
Решили как?
@Денис Гончаренко ну, я под другим шаблонизатором в итоге сделал, но если я все так понимаю, что-то не так с repo
@Денис Гончаренко , вышло решить?) Если да, то как, тоже заменой шаблонизатора? И я не понимаю в чем суть предупреждения, он имеет ввиду, что в "... @GetMapping("{user}")
public String userEditForm(@PathVariable User user, Model model) {... " переменная user является строкой и ее нельзя преобразовать в класс User?
@Денис Гончаренко А, окей спасибо, ну я так и думал реализовать, интересно было, почему не выходит как у автора, но может это из-за конфликта версий как-нибудь или еще чего-то
@Денис Гончаренко вот так будет менее криво)
@GetMapping("{userId}")
public String userEditForm(@PathVariable Long userId, Model model) {
User user = userDao.findById(userId).orElse(null);
model.addAttribute("user", user);
model.addAttribute("roles", Role.values());
return "userEdit";
}
Чтобы получить значения чекбоксов Можно было вместо requestParam form, получить массив значений выбранных чекбоксов и тогда работа упрощается.
В html параметру name для чекбоксов присвоить одинаковое имя "roles[]":
А в контроллере написать:
public String userSave(@RequestParam String username, @RequestParam(name="roles[]", required = false) String[] roles, @RequestParam("userId") User user){
user.setUsername(username);
user.getRoles().clear();
if(roles!=null) {
Arrays.stream(roles).forEach(r -> user.getRoles().add(Role.valueOf(r)));
}
userRepository.save(user);
return "redirect:/user";
Но спасибо, что показали как получать всю форму как параметр. Пригодится в будущем
О Боги, вы даже не представляете насколько я благодарна вашему комментарию))))))
Ты просто царь, я смог додуматься, как сделать, чтобы чекбокс работал, но не смог додуматься, как сделать чтобы пост отправка у чекбоксов работала, так как у меня @RequestParam Map form почему-то не работает
славься мил человек, помогла распутать клубок безобразия
Спасибо за такой бесценный коментарий!
У меня возникла проблема - 4:30 автор говорит, что спринг умеет: заменяет Long в User.
У меня же выскакивает ошибка в преобразовании. Нужны дополнительные библиотеки?
Могу только догадываться, какая именно ошибка у вас возникает, но я тоже тут застрял, пока не осознал, что userEdit.ftlh создал в parts, а не в templates. После исправления этого косяка все заработало.
У меня в проекте не 3 поля, а 7. Поэтому я передавал параметры с помощью аннотации @ModelAttribute. Так гораздо короче получается, да и правильнее, наверное. Чуть не забыл, так потом удобнее проверку валидности формы проводить. P.S. автор, где видео про "защиту от дурака" при отправке на сервер пустых полей?
@PostMapping
public String userSave(@ModelAttribute("user") User user,
@RequestParam(name="roles[]", required = false) String[] roles) {
user.getRoles().clear();
if(roles!=null) {
Arrays.stream(roles).forEach(r -> user.getRoles().add(Role.valueOf(r)));
}
userService.saveUser(user);
return "redirect:/users";
}
А можно попросить видео по веб сокетам на примере чатика с разными валидациями и т.д.?
Про Сокеты есть видос, но там не чатик, а нотификации. Но сделать чатик не сильно сложно, на основании моего видоса
Отличное видео! Отличны канал!
У меня есть один вопрос. Ребята, не могли бы вы объяснить новичку следующее: в классе UserController мы добавили @RequestMapping("/user"). Как я понимаю, "/user" это должен быть шаблон с названием "user". Но, мы же не создавали такую вьюшку? Тогда, куда ссылается @RequestMapping("/user")?
Заранее спасибо!
Нет, представь что Mapping("/user") - это как маркер, например, из goto, если знакома такая команда, в том числе @GetMapping и @PostMapping. Возьмем третий урок. Когда мы обращаемся через пост запрос, например такой из main.mustache(mustache более подходит для примера):
Список сообщений
то мы отправляем форму в пост метод(@PostMapping) с маркером "filter" (@PostMapping("/filter")).
Аннотация же @RequestMapping("/user") применяет маркер "user" к гет и пост методу данного класса. Это просто сокращение, вместо того, чтоб писать в трех местах @PostMapping("/user"), @GetMapping("/user"), @GetMapping("/user/{user}") мы пишем его в одном месте, тем самым сокращая адрес строки.
"Тогда, куда ссылается @RequestMapping("/user")?" - так что он не ссылается никуда, вообще маппинги никуда не ссылаются. Когда Spring стартует, он сканирует все приложение на аннотации и создает экземпляры нужных классов(они называются бины) и помещает в свой контекст(хранилище) и вот там уже происходит вся работа(иначе как мы запросами из страничек ссылаемся на не статик методы классов?). Я всего лишь джун и могу ошибаться в тонкостях, но примерно это выглядит так.
Если это вызвало трудности и нет уверенного знания как html общается с сервером, то лучше приостановить Spring и недельку посвятить чистому jpa и jdbc(Ultimate версия позволяет делать такие приложения в пару кликов). После этого будет полное понимание что и как устроено, без основ дальше смотреть смысла нет, я именно так и делал.
На форуме джавараша есть шикарные статьи, которые помогут подготовиться к изучению спринга.
@@user-ot1hf9ov6s Благодарю Вас за ответ. Пожалуй, так и сделаю, попробую разобраться перед тем, как идти дальше.
@@alisheruluknazarov5106 @RequestMapping("/user") - /user это не вьюшка, а путь. по этому пути срабатывает контроллер. Вьюшки из контроллера могут посылаться, и во вьюшках указывается на какой путь будут данные отправлять на обработку когда ты их вводишь во вьюшку
Простым языком - это маппинг по которому работает данный контроллер, т.к. теперь тебе не надо писать в каждом методе на @GetMapping или @PostMapping в скобках урл который ты обрабатываешь данным контроллером
Уроки супер, подача отличная, за высвечивание горячих клавиш - отдельный респект, помогите разобраться с ошибкой 403
При попытке установить пользователю роль или сменить ник, выводит ошибку 403, свой код сверил с кодом на GITе, самое интересно что @PostMapping userSave() вовсе не запускается...
сам разобрался) ошибка вот в чем, в файле userEdit.ftlh у автора value="_csrf.token" а нужно value="${_csrf.token}"
А как удалить объект из БД?
не нашел ответа в комментариях, при обработке Пост запроса выдает
Failed to convert value of type 'java.lang.String' to required type 'com.example.sweater.domain.User'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.sweater.domain.User': no matching editors or conversion strategy found
такое ощущение, что в строке @RequestParam("userId") User user спринг не понимает что нужно достать пользователя из базы, как это можно заменить чтоб работало?
Такая же проблема была с Гет запросом, но там решил с помощью одного из комментариев указав в параметрах Long userId и достав в теле метода этого пользователя из репозитория по номеру. Тут не пойму как сделать аналогично.
Помогите пожалуйста, голову сломал.
...
@PostMapping
public String userSave(
@RequestParam("username") String username,
@RequestParam Map form,
@RequestParam ("userId") User user
) {
...
@@ihorhryniv160 я справился через получение Long id клиента и доставание его из базы по этому id
@@sharky_47 поделись кодом, если не сложно
почему важен порядок name и value:
, но
т.е. в одном случае сначала передаем value затем name, в другом случае - наоборот?
Порядок не важен
Спасибо, из-за тебя я нашел ошибку!
Здравствуйте! Почему вы не добавили аннотацию EnableWebMvc к классу WebSecurityConfig?
Потому что EnableWebMvc не имеет отношения к Security и она по дефолту, как я понимаю, включена в SpringBoot.
С этой аннотацией затем нужно дополнительный код реализовать, к примеру код, который указывает где лежат статические ресурсы, и прочее. Я 2 дня искал почему у меня стили и иконки не работают после добавления этой аннотации. Если нужно настроить какую-то экзотическую архитектуру ресурсов тогда можно, но по дефолту лучше не стоит, особенно при обучении.
Здравствуйте , тут такая возникла проблема : при добавлении нового сообщения в список выдает ошибку. ( Ваш код)
FreeMarker template error (DEBUG mode; use RETHROW in production!):The following has evaluated to null or missing:==> filter [in template
when-presentwhen-missing.
это часть ошибки и по этой части непонтно ничего)
попробуй вот это в MainController
@PostMapping("/main")
public String addMessage(
@RequestParam(required = false, defaultValue = "") String filter,
@AuthenticationPrincipal User user,
@RequestParam String text,
@RequestParam String tag,
Model model) {
Message message = new Message(text, tag, user);
repository.save(message);
Iterable messages = repository.findAll();
model.addAttribute("messages", messages);
model.addAttribute("filter", filter);
return "main";
}
Андрей, подскажите пожалуйста. А как эту строку " ${user.roles?seq_contains(role)?string("checked", "")" написать в thymeleaf?
@@MrColizium респект и благодарность!
Огромное спасибо за подробные и понятные видео!!!
Есть один вопрос. Скачал код из репозитория, все запускается и работает, но при нажатии на ссылку User list получается вот такая ошибка:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Nov 22 14:35:29 MSK 2018
There was an unexpected error (type=Forbidden, status=403).
Forbidden
@Mihail Vilms сначала закомментируй @PreAuthorize("hasAuthority('ADMIN')") в UserController, далее под любым юзером логинся, и в userList через edit дай хоть одному юзеру админские права. Далее, из под админа можно на UserList попасть, из простого юзера - нет (не забудь раскомментировать @PreAuthorize)
@Mihail Vilms имел подобную проблемку, проверьте в Role метод getAuthority...
@@vladimirchevalier3486 у меня аналогичная проблема, подскажите пжл, что именно надо проверить и сделать?
@@ilimsarykbaev5084 Дополню правильное уточнение Владимира выше.
Сам столкнулся с этой ошибкой и после проверки кода с Git-ом, выявилась ошибка в Role.java -> Getter изначально выглядел так:
public String getAuthority() {
return null;
}
И оказывается ничего не возвращал, следовательно и система не могла узнать авторизацию.
Правильно:
public String getAuthority() {
return name();
}
тоже выскочила такая проблема, у автора не хватало "/" в файле userEdit и также местами поменял name c value. Все четко заработало.
создал новый проэкт, и все создал по-новой, на новой созданной базе данных, нет ошибок в коде,
но на localhost:8080 не хочет открываться, открывает сразу localhost:8080/login
при вводе логин и пасс выводит
Your login attempt was not successful, try again.
Reason: UserDetailsService returned null, which is an interface contract violation
При этом нет ни user list, ничего.
Только
Login with Username and Password, поля для ввода и кнопки login
Ну так смотреть надо, почему сервис возвращает null.
разобрался в чем дело?
как зайти под админом? идентичный код, нет штуки, где я могу сделать какогото пользователя админом
Простейший способ - зайти в БД и добавить нужному пользователю роль админ. А вообще мы же миграцией добалювляли админа
при замене localhost:8080/main на /user выводит
Whitelabel Error Page
вместо userlist
Это не текст ошибки
Если все прочее сделано правильно, то предполагаю, что csrf забыли добавить.
Андрей, спасибо большое за уроки, не могли бы вы объяснить метод userSave класса UserController, а именно эти строчки:
user.setUsername(username);
Set roles = Arrays.stream(Role.values())
.map(Role::name)
.collect(Collectors.toSet());
user.getRoles().clear();
for (String key : form.keySet()) {
if (roles.contains(key)){
user.getRoles().add(Role.valueOf(key));
}
}
Спасибо большое за ваш труд!
user.setUsername(username); - устанавливаем пользователю имя, которое пришло с фронта
Set roles = Arrays.stream(Role.values())
.map(Role::name)
.collect(Collectors.toSet());
берем все существующие в приложении роли, преобразуем массив этих ролей в стрим (java stream api), где получаем имена ролей и полученый список имён складываем в set
user.getRoles().clear(); - очищаем персистентную коллекцию от установленных ролей
for (String key : form.keySet()) {
if (roles.contains(key)){
user.getRoles().add(Role.valueOf(key));
}
}
обходим список полей, пришедших от пользователя, проверяем, если имя какого-то поля является именем роли, то ищем такую роль в enum Role и устанавливаем эти роли пользователю.
Почему так: 1) коллекции у объектов, полученных через JPA не являются обычными коллекциями, их нельзя просто заменить, можно только обновлять. 2) чтобы не городить каких-то сложных проверок, мы просто берем всё поля, полученные от пользователя и проверяем, являются ли они ролями, если да - устанавливаем их пользователю, обновляя таким образом нашу персистентную коллекцию ролей
спасибо большое за пояснения!
Ловлю:
Resolved exception caused by Handler execution: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.example.sweater.domain.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'post'; nested exception is java.lang.NumberFormatException: For input string: "post "
Проверьте что типы полей в сущности User такие же как и у автора. У меня id был Integer, а не Long к примеру.
public String userEditForm(@PathVariable Long user, Model model){
model.addAttribute("user", userRepo.findById(user).get());
model.addAttribute("roles", Role.values());
return "userEdit";
я когда не работает и не могу понять в чём дело хоть убейте, качаю проект с гитхаба (по тэгу, соответствующему теме урока) и сравниваю со своим проектом - папка с папкой.
На Линуксе для этого использую программу Meld, на Windows есть WinMerge.
...
@PostMapping
public String userSave(
@RequestParam("username") String username,
@RequestParam Map form,
@RequestParam ("userId") User user
) {
...
Вот такая ошибка: Add new user FreeMarker template error (DEBUG mode; use RETHROW in production!): The following has evaluated to null or missing: ==> message [in template "registration.ftl" at line 6, column 7] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use when-presentwhen-missing
Как понял пока база путая не хочет добавлять пользователей не подскажете как исправить?)
как решил можешь написать?
Тот же вопрос. Как решил?
Решается это легко, в контроллере RegistrationController в параметр метода добавьте public String registration(@ModelAttribute ("message") String message ) вместо public String registration(), после чего можно корректно добавлять юзеров без ошибок.
igor hadarin Хз насколько актуально в 2020 году, но если у вас вдруг вылетает ошибка при запуске на 3:04
The following has evaluated to null or missing: ==> user.username в строке ${user.username}, то необходимо сделать следующее:
${user.username!"null or missing"}
Такое может произойти, если вдруг у вас есть пустая запись (пользователь без имени) в БД
@@motiversia4714 Prosto Bozhenka
У меня почему то:
1) в userEdit.ftl для первого input'a ingellij idea подписывает missing associated label;
2) в userEdit.ftl
не видит откуда взять roles, хотя в model передаю. И по этому не могу напечатать наименования ролей, получаются пустые чекбоксы.
Может кто сталкивался - отзовитесь)
"Page editor
"
Если ещё актуально, то вы не в правильном месте закрыли тег input. У вас )}${role}>${role}
Куда писать если есть вопросы? (У меня проблема с передачей данных в форму для изменения)
в милицию!
Отличное видео.
Вроде все хорошо, ровно до попытки подключения bootstrap(Неплохо бы было посмотреть, как подключать к нашему шаблону это дело т.к. все с ним выходит довольно странно), так что добавите этот момент?
И теперь не работает добавление новых пользователей таки :)
Да, со стилями что-нибудь придумаю. А про регистрацию уже писали. Вот так лечится:
Нужно в темплейт registration.ftl заменить
${message} на ${message?ifExists}
А можете на словах сказать как подключить?
Если подключать bootstrap через porm -> "webjars/..." + править в WevSecurityConfig , то всё работает
Но как только пытаешься подключить любой внешний css/js(не только bootstrap, но и другое. Даже лежащий рядом), то ничего не работает
Все дело в пути? как его прописывать тогда вWSC? или где-то еще
Вот, как подключить в общем css к допустим к common.ftl пусть даже любой рядом лежащий
Нужна многоходовка. На эту тему будет видео ближайшую неделю-полторы)
кратко: добавить мэппинг статических ресурсов в MvcConfig в виде метода
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
потом добавить разрешение на запрос статики в конфиге web security:
.antMatchers("/static/**").permitAll()ну и создать в resources директорию static, куда ложить статику. Обращаться к ней из темплейтов например так:
/static/some_style.css
Большое спасибо, а то столько всего перерыл, но ответы слишком неупорядочены, уж думал через bean лезть.
Всё прекрасно работает)
Код полностью совпадает с Вашим. Подскажите пожалуйста что может быть При переходе к userEdit выскакивает ошибка:
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'com.dzenlab.sweater.domain.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable com.dzenlab.sweater.domain.User] for value '43'; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Provided id of the wrong type for class com.dzenlab.sweater.domain.User. Expected: class java.lang.Long, got class java.lang.Integer; nested exception is java.lang.IllegalArgumentException: Provided id of the wrong type for class com.dzenlab.sweater.domain.User. Expected: class java.lang.Long, got class java.lang.Integer
Если поменять поле Long id в классе User на Integer то все работает. Помогите пожалуйста разобраться. Заранее Благодарен.
The following has evaluated to null or missing:
Failed at: ${message} [in template "registration.ftlh" at line 5, column 5] что не так? я уже с гита скопировал все, не помогает
${message!} попробуй с воскл знаком
Если здесь есть кто смотрит это в 21 году,у меня такой вопрос у меня не сохранялись изменения с Postmapping,я решил убрать его и посмотреть в чем ошибка и все заработало...
Подскажите ,почему при переходе на /user может вылетать 404 ошибка ?Spring не видит класс UserController?
Я только начал смотреть видик но вангую что скорее всего ты либо ошибаешся в название шаблона либо в делаешь запрос на не правельный аддресс. 404 означает "не найдено"
Если 404 проверь все файл и аддрессы чтоб совпадали!
Еслт 403 то это хрень с CSRF'ом! Это просто в каждую форму (тэг в html, ) ляпни эту строку!