|
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