Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

JJUG CCC 2017 Spring LT about Twice Submit

518 Aufrufe

Veröffentlicht am

LT about Twice Submit

Veröffentlicht in: Ingenieurwesen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

JJUG CCC 2017 Spring LT about Twice Submit

  1. 1. 2重送信問題対策を Thymeleafで使えるようにした話 Naoya KOJIMA @jugemix
  2. 2. Qiita 記事で初めて知った2重送信問題 • 送信ボタンをダブルクリックするとリクエストを2回送信する • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった
  3. 3. Qiita 記事で初めて知った2重送信問題 • 送信ボタンをダブルクリックするとリクエストを2回送信する • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった 僕、実装してないや。 ヤバい・・・(^_^;)
  4. 4. 2重送信問題の対策 • 送信ボタンをダブルクリックするとリクエストを2回送信する • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった Qiitaに模範解答があったので 参考にする
  5. 5. 2重送信問題の対策 • 送信ボタンをダブルクリックすると、リクエストを2回送信す る • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった こんなことが起きないように 例外ハンドリング頑張る
  6. 6. 2重送信問題の対策 • 送信ボタンをダブルクリックすると、リクエストを2回送信す る • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった Qiita に答えが載ってない・・・
  7. 7. 2重送信問題の対策 • 送信ボタンをダブルクリックすると、リクエストを2回送信す る • 送信ボタンを押した後、ブラウザ戻るボタンを押してからもう 一度送信すると2回送信したことになる • 送信ボタンを押した後の画面にエラーが表示されたのでもう一 度送信を試みると、実は2回目の送信だった 遷移毎に発行したワンタイムトークンを 都度チェックすれば良いみたいだけど・・・ CSRFトークンはワンタイムトークンじゃないか ら使えない・・・
  8. 8. 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
  9. 9. TransactionTokenCheck の使い方 @TransactionTokenCheck("hogehoge") @RequestMapping("/hogehoge") @SessionAttributes(“hogehogeSession") @Controller public class HogehogeController { /* 中略 */ } 1. Hogehoge名前空間を作る
  10. 10. 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名前空間にトークン を発行する
  11. 11. TransactionTokenCheck の使い方 @TransactionTokenCheck @RequestMapping(value = "register", params = "_event_confirmed", method = RequestMethod.POST) String updateForm(@ModelAttribute("hogehogeSession") HogehogeForm hogehogeForm) throws Exception { /* 中略 */ return "redirect:complete"; } 3. Hogehoge名前空間に作った トークンを更新する
  12. 12. TransactionTokenCheck の使い方 @RequestMapping(value = "complete", method = RequestMethod.GET) String showCompleteForm(@ModelAttribute("hogehogeSession") HogehogeForm hogehogeForm, Model model, SessionStatus sessionStatus) { /* 中略 */ return "complete"; } 4. 最後は何も書かない
  13. 13. 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 すごく便利!
  14. 14. 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用だった
  15. 15. 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); } }
  16. 16. 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); /* 後略 */ }
  17. 17. Terasoluna ありがとうございます! • これからも頑張って下さい! • 僕こんな風にやってるよーなんて方、この後飲みながら一緒に 話しましょう!

×