|
1 | 1 | # 4.4 Дублирование отправки
|
2 | 2 |
|
3 |
| -Не знаю, встречали ли Вы, как на каком-либо блоге или форуме размещено несколько постов подряд с одинаковым содержимым, но я могу сказать Вам, что это происходит по причине того, что отправка постов дублируется пользователем. Это может произойти по многим причинам; иногда пользователь отправляет форму двойным щелчком, или он после отправки решает исправить содержимое поста и нажимает кнопку браузера "Назад". А иногда это - намереные действия злоумышленников. Понятно, что дублирование отправки может привести ко многим проблемам. Поэтому нам нужно принимать эффективные меры для его предотвращения. |
| 3 | +Не знаю, встречали ли Вы, как на каком-либо блоге или форуме размещено несколько постов подряд с одинаковым содержимым, но я могу сказать Вам, что это происходит по причине того, что отправка постов дублируется пользователем. Это может произойти по многим причинам; иногда пользователь отправляет форму двойным щелчком, или он после отправки решает исправить содержимое поста и нажимает кнопку браузера "Назад". А иногда это - намеренные действия злоумышленников. Понятно, что дублирование отправки может привести ко многим проблемам. Поэтому нам нужно принимать эффективные меры для его предотвращения. |
4 | 4 |
|
5 |
| -Решением этой задачи является добавление в форму скрытого поля с уникальным токеном и проверка этого токена перед перед обработкой введенных данных. А если для отправки формы Вы используете Ajax, можно после того, как данные отправлены, сделать кнопку отправки неактивной. |
| 5 | +Решением этой задачи является добавление в форму скрытого поля с уникальным токеном и проверка этого токена перед обработкой введенных данных. А если для отправки формы Вы используете Ajax, можно после того, как данные отправлены, сделать кнопку отправки неактивной. |
6 | 6 |
|
7 | 7 | Давайте усовершенствуем пример из раздела 4.2:
|
8 |
| - |
| 8 | +```html |
9 | 9 | <input type="checkbox" name="interest" value="football">Футбол
|
10 | 10 | <input type="checkbox" name="interest" value="basketball">Баскетбол
|
11 | 11 | <input type="checkbox" name="interest" value="tennis">Теннис
|
12 | 12 | Имя:<input type="text" name="username">
|
13 | 13 | Пароль:<input type="password" name="password">
|
14 | 14 | <input type="hidden" name="token" value="{{.}}">
|
15 | 15 | <input type="submit" value="Login">
|
16 |
| - |
| 16 | +``` |
17 | 17 | Для того, чтобы сгенерировать токен, мы используем хэш MD5 (временная отметка), и добавляем его как в скрытое поле формы ввода данных на стороне клиента, так и в сессионный куки на стороне сервера (см. Раздел 6). Мы можем использовать этот токен для того, чтобы проверить, отправлялись ли уже данные с этой формы:
|
18 |
| - |
19 |
| - func login(w http.ResponseWriter, r *http.Request) { |
20 |
| - fmt.Println("method:", r.Method) // получаем метод запроса |
21 |
| - if r.Method == "GET" { |
22 |
| - crutime := time.Now().Unix() |
23 |
| - h := md5.New() |
24 |
| - io.WriteString(h, strconv.FormatInt(crutime, 10)) |
25 |
| - token := fmt.Sprintf("%x", h.Sum(nil)) |
26 |
| - |
27 |
| - t, _ := template.ParseFiles("login.gtpl") |
28 |
| - t.Execute(w, token) |
29 |
| - } else { |
30 |
| - // запрос данных о входе |
31 |
| - r.ParseForm() |
32 |
| - token := r.Form.Get("token") |
33 |
| - if token != "" { |
34 |
| - // проверяем валидность токена |
35 |
| - } else { |
36 |
| - // если нет токена, возвращаем ошибку |
37 |
| - } |
38 |
| - fmt.Println("username length:", len(r.Form["username"][0])) |
39 |
| - fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // печатаем на стороне сервера |
40 |
| - fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) |
41 |
| - template.HTMLEscape(w, []byte(r.Form.Get("username"))) // отвечаем клиенту |
42 |
| - } |
| 18 | +```Go |
| 19 | +func login(w http.ResponseWriter, r *http.Request) { |
| 20 | + fmt.Println("method:", r.Method) // получаем метод запроса |
| 21 | + if r.Method == "GET" { |
| 22 | + crutime := time.Now().Unix() |
| 23 | + h := md5.New() |
| 24 | + io.WriteString(h, strconv.FormatInt(crutime, 10)) |
| 25 | + token := fmt.Sprintf("%x", h.Sum(nil)) |
| 26 | + |
| 27 | + t, _ := template.ParseFiles("login.gtpl") |
| 28 | + t.Execute(w, token) |
| 29 | + } else { |
| 30 | + // запрос данных о входе |
| 31 | + r.ParseForm() |
| 32 | + token := r.Form.Get("token") |
| 33 | + if token != "" { |
| 34 | + // проверяем валидность токена |
| 35 | + } else { |
| 36 | + // если нет токена, возвращаем ошибку |
| 37 | + } |
| 38 | + fmt.Println("username length:", len(r.Form["username"][0])) |
| 39 | + fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // печатаем на стороне сервера |
| 40 | + fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) |
| 41 | + template.HTMLEscape(w, []byte(r.Form.Get("username"))) // отвечаем клиенту |
43 | 42 | }
|
44 |
| - |
| 43 | +} |
| 44 | +``` |
45 | 45 | 
|
46 | 46 |
|
47 | 47 | Рисунок 4.4 Содержимое браузера после добавления токена
|
|
0 commit comments