Skip to content
Vladimir edited this page Feb 9, 2019 · 1 revision

Онлайн проекта Topjava

error Правка

Apply 10_0_fix.patch

  • Начальная страница недоступна авторизированным пользователям (только isAnonymous()) и если после логина перейти в браузере назад, получим "403 forbidden". Сделал ее доступным всем.

hw Разбор домашнего задания HW9

video 1. HW9

Apply 10_1_HW9_binding_ajax.patch

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()

Apply 10_2_HW9_test.patch

Apply 10_3_HW9_datetimepicker.patch

  • Изменил формат ввода dateTime в форме без 'T': при биндинге значений к полям формы в datatablesUtil.updateRow() для поля dateTime делаю replace('T', ' '). REST интерфейс по прежнему работает в стандарте ISO-8601
  • Вынес общий код в datatablesUtil.formatDate()
  • В новой версии datetimepicker работает ограничение выбора времени startTime/endTime

Занятие 10:

ВНИМАНИЕ! Патчи 10_04_opt и 10_05_opt - не обязательные! Если будете делать- то в отдельной ветке (у меня opt_view_groups). Это варианты решений, которые не идут в master.

Apply 10_04_opt_json_view.patch

Apply 10_05_opt_validated_groups.patch

ВНИМАНИЕ! далее патчи идут после 10_03_HW9_datetimepicker в ветке master

Apply 10_04_jquery_converters.patch

Apply 10_05_persist_validate_group.patch

  • Default Hibernate validation. Т.к. Persist наследуется от javax.validation.groups.Default, при сохранении учитываются все непомеченные аннотации валидации (Default) + помеченные Persist.

question При 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.

Apply 10_06_secure_tag_annotation.patch

Сделал общий bodyHeader.jsp с разделением isAnonymous/isAuthenticated

question Вопрос:

У нас в sprng-mvc подключается spring-app, в который подключается spring-security. Т.о. spring-mvc в себе содержит код spring-app и spring-security. Почему тогда аннотация <security:global-method-security... из spring-security не видна для контроллера из spring-mvc?

  1. Подключаем один контекст внутрь другого: это про import а не про наследование (тоже самое что скопипастить сюда xml из подключаемого import файла). Наследование означает, что поиск бина, если он не найден, происходит в родителе.
  2. При создании контекста аннотация <security:global-method-security... говорит про то, что все бины текущего контекста, у которых есть аннотации авторизации (@PreAuthorize/ @Secured/ ..) будут проксироваться с проверкой авторизации. Контексты родителей и детей создаются сами по себе и аннотации проксирования у них собственные.

Apply 10_07_interceptor.patch

Apply 10_08_profile_jsptag.patch

  • Упростил: в inputField.tag передаю для label код локализации
  • В профиль добавил кнопку "Cancel"

Apply 10_09_registration.patch

  • Добавил локализацию
  • При регистрации передаю username в login.jsp как параметр и делаю setCredentials (пароль вводится скрыто, поэтому его не передаю из соображений безопасности). Т.к setCredentials требует jQuery, убрал для него defer
  • В login.jsp кнопки входа и регистрации отображаю только для isAnonymous()
  • Добавил регистрацию пользователя по REST: ProfileRestController.register и тест

Apply 10_10_not_found_422.patch

Поменял код 404 (URL not found) на 422 (Unprocessable Entity)

Apply 10_11_global_exception.patch

  • Перед отображением exception предварительно делаю ValidationUtil.getRootCause
  • Добавил локализацию

Apply 10_12_controller_advice_exception.patch

Отображение валидации для формы еды и юзера пропало (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. Локализация ошибок будет на последнем занятии

Apply 10_13_password_encoding.patch

Apply 10_14_read_write_access.patch

В реальном приложении для управления паролем необходим отдельный UI интерфейс с подтверждением старого пароля.

Apply 10_15_csrf.patch

Убрал form:form из ajax запросов: там csrf работает через header. Проверьте в вкладке браузера Network.

Поломалась UTF-8 кодировка в редактировании профиля и регистрациию (если по умолчанию не UTF-8). В Optional HW10 нужно будет починить.

question Ваши вопросы

В чем отличие между аннотоацией @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)?

BCryptPasswordEncoder automatically generates a salt and concatenates it with the hash value in a single String.

Когда запускается в GlobalControllerExceptionHandler метод defaultErrorHandler? Когда как в него исключение попадает? Как выбирается, кто обрабатывает исключения: ExceptionInfoHandler или GlobalControllerExceptionHandler?

GlobalControllerExceptionHandler попадает в контекст спринг (через @ControllerAdvice его находят в пакете web). Далее спринг перехватывает исключения и отправляет в подходящий по исключению метод GlobalControllerExceptionHandler. ExceptionInfoHandler помечен @ControllerAdvice(annotations = RestController.class), он обрабатывает только ошибки из всех контроллеров с аннотацией RestController.

Откуда берутся в валидации сообщения на русском "должно быть между 10 и 10000"?

Локализация встроена в Hibernate Validation. Смотрите Ctrl+Shift+N и ValidationMessages_ru.properties.

hw Домашнее задание HW10

  • 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 контроллеров

Optional


error Проверка в HW10

  • 1: ErrorInfo просто бин для передачи информации на клиента. Кода возврата и ответ настраиваются в ExceptionInfoHandler.
  • 2: Не дублируйте обработку ошибок BindingResult: result.getFieldErrors()..
  • 3: Можно не создавать собственные эксепшены, а в ExceptionInfoHandler ловить стандартные
  • 4: в MethodArgumentNotValidException также есть e.getBindingResult(), его можно обрабатывать по аналогии с BindException
  • 5: Не дублируйте код переключения локали на странице логина и в приложении
  • 6: При проблемах с валидацией Meals в MealRestController, посмотрите на валидацию в MealAjaxController.updateOrCreate

Clone this wiki locally