-
Notifications
You must be signed in to change notification settings - Fork 35
Lesson 10
Онлайн проекта Topjava
- Начальная страница недоступна авторизированным пользователям (только
isAnonymous()) и если после логина перейти в браузере назад, получим "403 forbidden". Сделал ее доступным всем.
1. HW9
Datatables перевели на ajax ("ajax": {"url": ajaxUrl, ..), те при отрисовке таблица сама по этому url запрашивает данные. Поэтому в методе RootController.meals() нам нужно только возвратить view "meals" (meals.jsp) которому уже не нужны данные в атрибутах.
- JavaScript
i18n[]локализацию перенес вi18n.jspи передаю тудаpageкак параметр- Вынес общий код в
ValidationUtil.getErrorResponse()- Вынес создание
DataTableвtopjava.common.js. В параметр конфигурации добавляю общие опции используя jQuery.extend()
- Изменил формат ввода dateTime в форме без 'T': при биндинге значений к полям формы в
datatablesUtil.updateRow()для поляdateTimeделаюreplace('T', ' '). REST интерфейс по прежнему работает в стандарте ISO-8601- Вынес общий код в
datatablesUtil.formatDate()- В новой версии
datetimepickerработает ограничение выбора времениstartTime/endTime
ВНИМАНИЕ! Патчи 10_04_opt и 10_05_opt - не обязательные! Если будете делать- то в отдельной ветке (у меня opt_view_groups). Это варианты решений, которые не идут в master.
ВНИМАНИЕ! далее патчи идут после 10_03_HW9_datetimepicker в ветке master
-
Default Hibernate validation. Т.к.
Persistнаследуется отjavax.validation.groups.Default, при сохранении учитываются все непомеченные аннотации валидации (Default) + помеченныеPersist.
При
NotNull(groups = View.Persist.class)валидация происходит непосредственно перед созданием или редактированием на уровне репозиториев? А если бы мы установили без группы то проверка была сразу после получения с UI?
Если View никаких нет (что равнозначно javax.validation.groups.Default), то бины, в которых есть аннотации валидации, проверяют Spring в контроллерах (если перед параметром стоит @Valid) и Hibernate перед сохранением и обновлением. Через View мы можем управлять валидацией. В нашем случае мы включили в spring-db.xml указание Hibernate валидировать поля с View.Persist. Так как View.Persist наследуется от Default, то Hibernate будет также валидировать и поля, на которых нет View.
Сделал общий
bodyHeader.jspс разделениемisAnonymous/isAuthenticated
У нас в
sprng-mvcподключаетсяspring-app, в который подключаетсяspring-security. Т.о.spring-mvcв себе содержит кодspring-appиspring-security. Почему тогда аннотация<security:global-method-security...изspring-securityне видна для контроллера изspring-mvc?
- Подключаем один контекст внутрь другого: это про import а не про наследование (тоже самое что скопипастить сюда xml из подключаемого import файла). Наследование означает, что поиск бина, если он не найден, происходит в родителе.
- При создании контекста аннотация
<security:global-method-security...говорит про то, что все бины текущего контекста, у которых есть аннотации авторизации (@PreAuthorize/ @Secured/ ..) будут проксироваться с проверкой авторизации. Контексты родителей и детей создаются сами по себе и аннотации проксирования у них собственные.
- Упростил: в
inputField.tagпередаю для label код локализации- В профиль добавил кнопку "Cancel"
- Bootstrap 4 form validation example
- Делаем jsp tag для ввода поля формы.
- Spring MVC Form Validation
- Spring MVC Form: check if a field has an error
- Добавил локализацию
- При регистрации передаю
usernameвlogin.jspкак параметр и делаюsetCredentials(пароль вводится скрыто, поэтому его не передаю из соображений безопасности). Т.кsetCredentialsтребует jQuery, убрал для негоdefer- В
login.jspкнопки входа и регистрации отображаю только дляisAnonymous()- Добавил регистрацию пользователя по REST:
ProfileRestController.registerи тест
Поменял код 404 (URL not found) на 422 (Unprocessable Entity)
- Перед отображением exception предварительно делаю
ValidationUtil.getRootCause- Добавил локализацию
Отображение валидации для формы еды и юзера пропало (TODO в коде для HW10). Можно посмотреть сериализацию в ErrorInfo при попытке добавить юзера с дублирующимся email.
- Добавил в
ErrorInfoтип ошибкиErrorType- Добавил обработку неверного запроса (
IllegalRequestDataException)@ResponseBodyнад методамиExceptionInfoHandlerзаменил на@RestControllerAdvice- В
ExceptionInfoHandlerудалил@Orderнад методами и добавил над классом:
Methods are matched by closest exception in
* @see org.springframework.web.method.annotation.ExceptionHandlerMethodResolver#getMappedMethod
* 164: Collections.sort(matches, new ExceptionDepthComparator(exceptionType))
- В
ExceptionInfoHandler.logAndGetErrorInfo()также используюValidationUtil.getRootCause- Добавил в
curl.mdпример с возвращениемErrorInfo. Локализация ошибок будет на последнем занятии
- Сериализация Exception в JSON
- Exception Handling на уровне контроллера
- @ControllerAdvice improvements in Spring 4
- @Valid @RequestBody + Error handling
В реальном приложении для управления паролем необходим отдельный UI интерфейс с подтверждением старого пароля.
Убрал
form:formиз ajax запросов: там csrf работает через header. Проверьте в вкладке браузера Network.
Поломалась UTF-8 кодировка в редактировании профиля и регистрациию (если по умолчанию не UTF-8). В Optional HW10 нужно будет починить.
- Межсайтовая подделка запроса (CSRF)
- Using Spring Security CSRF Protection
- Ajax and JSON Requests
- Stateless CSRF protection
- Ресурсы:
В чем отличие между аннотоацией
@PreAuthorize("hasRole('ROLE_ADMIN')")и конфигурацией в jsp:<sec:authorize access="isAuthenticated()">,<sec:authorize access="hasRole('ROLE_ADMIN')">?
Анотация @PreAuthorize обрабатывается Spring анологично @Transactional, @Cacheable - класс проксируется и до-после вызова метода добавляется функциональность.
В данном случае перед вызовом метода проверяются роль залогиненного юзера. JSTL тэг authorize выполняет проверку условия в залогиненном юзере внутри jsp.
Еще раз: почему не нужен csrf для REST и нельзя подделать JSON запрос с вредоносного сайта?
Попробуйте выполнить ajax запрос из вашего приложения c url, у которого домен отличный от вашего (например "http://topjava.herokuapp.com/meals/ajax/admin/users/"+id).
В консоли браузера будет XMLHttpRequest cannot load... - нарушение same origin policy.
Формам же разрешается делать submit (через action=..) на другой домен, но невозможно cделать Content-Type, отличный от стндартных enctype и методов кроме get и post. Таким образом consumes = MediaType.APPLICATION_JSON_VALUE в POST защищает приложение от CSRF.
Почему использован
BCryptPasswordEncoderа неhash(password+salt)?
Когда запускается в
GlobalControllerExceptionHandlerметодdefaultErrorHandler? Когда как в него исключение попадает? Как выбирается, кто обрабатывает исключения:ExceptionInfoHandlerилиGlobalControllerExceptionHandler?
GlobalControllerExceptionHandler попадает в контекст спринг (через @ControllerAdvice его находят в пакете web). Далее спринг перехватывает исключения и отправляет в подходящий по исключению метод GlobalControllerExceptionHandler. ExceptionInfoHandler помечен @ControllerAdvice(annotations = RestController.class), он обрабатывает только ошибки из всех контроллеров с аннотацией RestController.
Откуда берутся в валидации сообщения на русском "должно быть между 10 и 10000"?
Локализация встроена в Hibernate Validation. Смотрите Ctrl+Shift+N и ValidationMessages_ru.properties.
- 1: Сделать валидацию в AdminAjaxController/MealAjaxController через
ExceptionInfoHandler. Вернуть клиентуErrorInfoи статусHttpStatus.UNPROCESSABLE_ENTITY(тип методов контроллеров вернуть обратно наvoid). - 2: Сделать валидацию принимаемых json объектов в REST контроллерах через
ExceptionInfoHandler. Добавить в Rest контроллеры тест для невалидных данных. - 3: Сделать обработку ошибки при дублирования email ("User with this email already exists") для:
- 3.1 регистрации / редактирования профиля пользователя
- 3.2 добавления / редактирования пользователя в таблице
- 3.3 REST контроллеров
- 4: Сделать обработку ошибки при дублирования dateTime еды. Сделать тесты на дублирование email и dateTime.
- 5: Сделать в приложении выбор локали (см. http://topjava.herokuapp.com/)
- 6: Починить UTF-8 в редактировании профиля и регистрации (если кодировка по умолчанию у вас не UTF-8). Подумайте, почему кодировка поломалась.
- 1:
ErrorInfoпросто бин для передачи информации на клиента. Кода возврата и ответ настраиваются вExceptionInfoHandler. - 2: Не дублируйте обработку ошибок
BindingResult:result.getFieldErrors().. - 3: Можно не создавать собственные эксепшены, а в
ExceptionInfoHandlerловить стандартные - 4: в
MethodArgumentNotValidExceptionтакже естьe.getBindingResult(), его можно обрабатывать по аналогии сBindException - 5: Не дублируйте код переключения локали на странице логина и в приложении
- 6: При проблемах с валидацией
MealsвMealRestController, посмотрите на валидацию вMealAjaxController.updateOrCreate
Правка
Разбор домашнего задания HW9
При