17
2重送信問題対策を Thymeleafで使えるようにした話 Naoya KOJIMA @jugemix

JJUG CCC 2017 Spring LT about Twice Submit

Embed Size (px)

Citation preview

Page 1: JJUG CCC 2017 Spring LT about  Twice Submit

2重送信問題対策をThymeleafで使えるようにした話

Naoya KOJIMA @jugemix

Page 2: JJUG CCC 2017 Spring LT about  Twice Submit

Qiita記事で初めて知った2重送信問題

•送信ボタンをダブルクリックするとリクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

Page 3: JJUG CCC 2017 Spring LT about  Twice Submit

Qiita記事で初めて知った2重送信問題

•送信ボタンをダブルクリックするとリクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

僕、実装してないや。ヤバい・・・(^_^;)

Page 4: JJUG CCC 2017 Spring LT about  Twice Submit

2重送信問題の対策

•送信ボタンをダブルクリックするとリクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

Qiitaに模範解答があったので参考にする

Page 5: JJUG CCC 2017 Spring LT about  Twice Submit

2重送信問題の対策

•送信ボタンをダブルクリックすると、リクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

こんなことが起きないように例外ハンドリング頑張る

Page 6: JJUG CCC 2017 Spring LT about  Twice Submit

2重送信問題の対策

•送信ボタンをダブルクリックすると、リクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

Qiita に答えが載ってない・・・

Page 7: JJUG CCC 2017 Spring LT about  Twice Submit

2重送信問題の対策

•送信ボタンをダブルクリックすると、リクエストを2回送信する

•送信ボタンを押した後、ブラウザ戻るボタンを押してからもう一度送信すると2回送信したことになる

•送信ボタンを押した後の画面にエラーが表示されたのでもう一度送信を試みると、実は2回目の送信だった

遷移毎に発行したワンタイムトークンを都度チェックすれば良いみたいだけど・・・

CSRFトークンはワンタイムトークンじゃないから使えない・・・

Page 8: JJUG CCC 2017 Spring LT about  Twice Submit

Terasolunaに答えがあった!

<dependency>

<groupId>org.terasoluna.gfw</groupId>

<artifactId>terasoluna-gfw-web</artifactId>

<version>5.3.0.RELEASE</version>

</dependency>

org.terasoluna.gfw.web.token.transaction.TransactionTokenCheck.class

Page 9: JJUG CCC 2017 Spring LT about  Twice Submit

TransactionTokenCheckの使い方

@TransactionTokenCheck("hogehoge")

@RequestMapping("/hogehoge")

@SessionAttributes(“hogehogeSession")

@Controller

public class HogehogeController {

/* 中略 */

}

1. Hogehoge名前空間を作る

Page 10: JJUG CCC 2017 Spring LT about  Twice Submit

TransactionTokenCheckの使い方

@TransactionTokenCheck(type = TransactionTokenType.BEGIN)

@RequestMapping(value = "enter", params = "_event_entered",

method = RequestMethod.POST)

String verifyForm(@Validated HogehogeForm hogehogeForm, BindingResult result, Model model) {

/* 中略 */

return "confirm";

}

2. Hogehoge名前空間にトークンを発行する

Page 11: JJUG CCC 2017 Spring LT about  Twice Submit

TransactionTokenCheckの使い方

@TransactionTokenCheck

@RequestMapping(value = "register", params = "_event_confirmed",

method = RequestMethod.POST)

String updateForm(@ModelAttribute("hogehogeSession") HogehogeFormhogehogeForm) throws Exception {

/* 中略 */

return "redirect:complete";

}

3. Hogehoge名前空間に作ったトークンを更新する

Page 12: JJUG CCC 2017 Spring LT about  Twice Submit

TransactionTokenCheckの使い方

@RequestMapping(value = "complete", method = RequestMethod.GET)

String showCompleteForm(@ModelAttribute("hogehogeSession") HogehogeForm hogehogeForm, Model model, SessionStatussessionStatus) {

/* 中略 */

return "complete";

}

4. 最後は何も書かない

Page 13: JJUG CCC 2017 Spring LT about  Twice Submit

Terasolunaに答えがあった!

<dependency>

<groupId>org.terasoluna.gfw</groupId>

<artifactId>terasoluna-gfw-web</artifactId>

<version>5.3.0.RELEASE</version>

</dependency>

org.terasoluna.gfw.web.token.transaction.TransactionTokenCheck.class

すごく便利!

Page 14: JJUG CCC 2017 Spring LT about  Twice Submit

Terasolunaに答えがあった!

<dependency>

<groupId>org.terasoluna.gfw</groupId>

<artifactId>terasoluna-gfw-web</artifactId>

<version>5.3.0.RELEASE</version>

</dependency>

org.terasoluna.gfw.web.token.transaction.TransactionTokenCheck.class

でもトークン生成がJSP用だった

Page 15: JJUG CCC 2017 Spring LT about  Twice Submit

Thymeleaf用を作ってみた

// ダイアレクト

public class ThymeleafDialect extends AbstractDialect {

/* 中略 */

@Override

public String getPrefix() {

return PREFIX;

}

@Override

public Set<IProcessor> getProcessors() {

final Set<IProcessor> processors = new LinkedHashSet<IProcessor>();

processors.add(new FormProcessor());

return new LinkedHashSet<IProcessor>(processors);

}

}

Page 16: JJUG CCC 2017 Spring LT about  Twice Submit

Thymeleaf用を作ってみた

// プロセッサ

public class FormProcessor extends AbstractElementProcessor {

private void addTokenHiddenFields(Arguments arguments, Element element) {

/* 前略 */

String tokenName = TransactionTokenInterceptor.TOKEN_REQUEST_PARAMETER;

String tokenValue = nextToken.getTokenString();

httpSession.setAttribute(tokenName, tokenValue);

Element node = new Element("input");

node.setAttribute("type", "hidden");

node.setAttribute("name", tokenName);

node.setAttribute("value", tokenValue);

element.addChild(node);

/* 後略 */

}

Page 17: JJUG CCC 2017 Spring LT about  Twice Submit

Terasolunaありがとうございます!

•これからも頑張って下さい!

•僕こんな風にやってるよーなんて方、この後飲みながら一緒に話しましょう!