Что такое «правильный» или «безопасный» способ обновления сохраняемого объекта с помощью Spring MVC 3 + Hibernate? - PullRequest
3 голосов
/ 19 июля 2011

Учитывая очень простой объект:

class User {
    private Integer id;
    private String name;

    public User() {}

    public Integer getId() { return id; }
    public String getName() { return name; }

    public void setName(String name) { this.name = name; }
}

и очень простое действие контроллера:

@RequestMapping(value="/edit/{id}/**", method=RequestMethod.POST)
public String editFromForm(@PathVariable("id") Integer id, @Valid User user, BindingResult bindingResult, Model model) {
    // If we have errors, don't save
    if(bindingResult.hasErrors()) {
        // Put what they did in the model and send it back
        model.addAttribute(user);

        return "users/edit";
    } else {
        userDAO.save(user);
    }

    // Show them the updated page on success
    return "redirect:/users/" + user.getId() + "/" + user.getName();
}

и очень простую форму:

<sf:form method="POST" modelAttribute="user">
    <label for="user_name">Name:</label>
    <sf:input path="name" id="user_name" />
    <input type="submit" value="save" /><sf:errors path="name" cssClass="error" />              
</sf:form>

Как должен Обновлять сущность в базе данных?В настоящее время (поскольку saveOrUpdate() является фактическим вызовом гибернации за методом save() моего DAO, новый объект сохраняется вместо обновления существующего, поскольку поле id не устанавливается для объекта, созданного из представления формы.

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

  1. Вставьте идентификатор из параметра URL-адреса в объект, идущий из подшивки модели
  2. Имейте скрытое поле id в форме и позвольте подшивке модели присоединить идентификатор

В обоих случаях нет проверки, чтобы убедиться, что объект все тот же, такой как какая-то контрольная сумма. Как другие справляются с этим?Наглядный пример, который проходит через эту проблему?

Другая проблема, которая возникает, состоит в том, что я предпочел бы не нуждаться в методе setId(), так как Hibernateуправляет всеми идентификаторами.Из того, что я смог определить, связыватель модели Spring MVC может связывать поле только в том случае, если у него есть ожидаемые методы получения и установки.Есть ли другой способ применить новое состояние, например получить текущего пользователя из БД по параметру id URL-адреса, а затем применить к нему новое состояние, но без необходимости явного кодирования всех копий поля?

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

Я довольно новв Spring + Hibernate, так что прости меня, если это одна из тех обыденных, широко освещаемых тем, но я не смог найти ни одного четкого примера, который бы охватывал мою очень простую ситуацию.Если это достаточно покрыто , пожалуйста, укажите мне правильное направление.

Ответы [ 4 ]

4 голосов
/ 19 июля 2011

Пара возможных решений пришла ко мне, но я не уверен, что лучше с точки зрения поддержания чистоты и безопасности (так что злоумышленник не может просто запускать изменения в любом идентификаторе объекта, который он желает)).

Ни один из двух упомянутых вами подходов на самом деле не будет работать с пользователем, который пытается редактировать объекты, на которые у него нет прав доступа.В конце дня пользователь, отправляющий форму, должен сообщить вам, для какого объекта он отправляет данные - в параметре URL или в скрытом параметре формы.Я бы сказал, что из двух, которые вы выбираете, действительно зависит от стиля и личных предпочтений.

Но что вам нужно делать независимо от выбора, это проверять, авторизован ли пользователь, вошедший в данный момент.изменить объект при обработке отправки формы.Это будет означать, что вам нужно проверить, имеет ли этот пользователь право редактировать текущий идентификатор объекта, используя любую логику, которая включает «разрешено делать это» для вашего приложения.

2 голосов
/ 04 декабря 2012

Я немного опоздал, но, возможно, это будет кому-то полезно.Сегодня я решил ту же проблему.Я думаю, что ваша проблема была там:

} else {
    userDAO.save(user);
}

Если это был спящий сеанс, то новый идентификатор генерировался при каждом вызове.Я использовал

session.saveOrUpdate(user) 

вместо

session.save(user)
2 голосов
/ 19 июля 2011

Все еще есть пользователь POJO

class User {
    private Integer id;
    private String name;

    public User() {}

    public Integer getId() { return id; }
    public String getName() { return name; }

    public void setName(String name) { this.name = name; }
}


@RequestMapping("/create/userCreate.do")
    public String createUser(Model model) {
        User user = new User();
        model.addAttribute(user);

        return "userCreate";
    }

Здесь я не отправляю идентификатор или какой-либо параметр, просто передаю атрибут модели пользовательской формы

@RequestMapping(value="/edit/userEdit.do", method=RequestMethod.POST)
public String editFromForm(@ModelAttribute("user") @Valid User user, BindingResult bindingResult, Model model) {
    // If we have errors, don't save
    if(bindingResult.hasErrors()) {
        // Put what they did in the model and send it back
        model.addAttribute(user);

        return "users/edit";
    } else {
        userDAO.saveOrUpdate(user);
    }

    // Show them the updated page on success
    return "redirect:/users/" + user.getId() + "/" + user.getName();
}

Здесь объектом User является имя пользователя и идентификатор привязки. Если идентификатора нет, объект будет сохранен, а если объект пользователя уже имеет идентификатор, то он будет обновлен. Вам не нужно передавать идентификатор или параметр и делать его видимым в адресной строке. Иногда мне не нравится идея показывать параметры пользователям. Я надеюсь, что это помогает

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<head>
<title> User Edit
</title>
</head>
<body>
    <form:form method="post" action="userEdit.do" commandName="user">
        <form:hidden path="id" />
                    <form:label path="username">User name:</form:label>

                <form:input path="username" type="text" maxlength="20" />
                <form:errors path="username" />
    </form:form>
</body>
</html>
1 голос
/ 19 июля 2011

Я согласен на 100% с тем, что говорит @matt b.Я работал с подобными продуктами, где в конце дня у вас нет никакого способа узнать, редактирует ли пользователь нужный объект.Единственный вариант - убедиться, что пользователю разрешено это делать.Если у вас есть пользователь, который редактирует свой профиль, то, очевидно, он не должен редактировать другого человека.

Теперь самый простой способ сделать то, что вы пытаетесь сделать, это на самом деле лучше.Вы не хотите использовать URL или скрытые атрибуты.Вы должны иметь возможность использовать @ModelAttribute для этого.Вот пример

  @RequestMapping(method = RequestMethod.GET)
    public ModelAndView setupForm(@RequestParam("petId") int petId) {
        User user = ...
        model.addAttribute("user", user);
        return model;
    }

Тогда вы сможете сделать

  @RequestMapping(method = RequestMethod.POST)
    public ModelAndView submit(@ModelAttribute("user") User user) {
       ...
    }

Нет сеттеров или скрытых полей.Вся загрузка и привязка обрабатываются через Spring.Больше документации здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...