Top / Struts / 同期トークンで二度押しなどをチェックする

WEB開発で必ずついて回るのが、Submitボタン二度押しや戻るボタンを押されるなど、勝手な画面遷移をされないような配慮です。Strutsでは同期トークンという機能でこれらの考慮をサポートしてくれます。

実際にサンプルで、ある画面でSubmitを二度押ししたとき、それを検知して二つ目のリクエストをエラーではじくという事を考えてみます。

同期トークンとは

同期トークンの機能とは以下の通りです。

  1. あるアクションで、サーバ上でユニークなID(以下、トークン)を生成し、返却するhtmlにhidden等で仕込んでおく
  2. そのアクションで、トークンはSessionにも格納しておく
  3. 次のリクエストにはhidden内のトークンが飛んでくる
  4. 次のアクションで、hiddenパラメタ内のトークンとSessionのトークンが等しいことを確認する
  5. 等しければ、正しいリクエストということで処理する。Session内のトークンを新しいモノに書き換え、hiddenで返すトークンもその新しいモノにする。等しくなければ、正規のリクエストではないということで、エラー処理する。
  6. 以下繰り返し。

この中で、トークンの生成、トークンチェック、hiddenにトークンを書き出す、などはStrutsが自動でやってくれますので、使う側はトークンをチェックするメソッドをアクションクラスに記述したり、エラー処理だけをやっておけばよいと言うことですね。ラクチンです。

やってみる

流れ

index.do -> IndexAction -> index.jsp -> double.do -> DoubleAction -> success.jsp

という流れのサンプルです。

ソース

  • IndexAction?
    public ActionForward execute(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response)
        throws Exception {
      saveToken(request);
      return mapping.findForward("success");
    }
  • index.jsp
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <%@ page language="java" contentType="text/html; charset=UTF-8" %>
    <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
    <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
    <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
    
    <%@page import="org.apache.struts.Globals"%>
    <html:html xhtml="true" lang="true">
    <head>
    <title>同期トークンの稼動確認JSP</title>
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
    <html:base />
    </head>
    <body>
    <html:form method="post" action="/double" >
      <html:submit />
    </html:form>
    <%=session.getAttribute(Globals.TRANSACTION_TOKEN_KEY) %>
    </body>
    </html:html>
  • DoubleAction?
    public ActionForward execute(ActionMapping mapping, ActionForm form,
         HttpServletRequest request, HttpServletResponse response)
         throws Exception {
      ActionMessages errors = new ActionMessages();
      HttpSession session = request.getSession();
      boolean tokenValid = false;
      synchronized (session) {
        tokenValid = isTokenValid(request);
        saveToken(request);
      }
      if (!tokenValid) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("tokenチェックエラー");
        errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
                new String(buffer), false));
        saveErrors(request, errors);
      }
    }

セッションにトークンをセット

IndexAction?でSessionにトークンをセットしています。トークンを生成してSessionにセットするメソッドは

org.apache.struts.action.Action#saveToken

です*1*2

JSPのhiddenにトークンをセット

index.jspではformを使ってSubmitしていますが、出力されるhtmlには自動的にhiddenタグが挿入されます。具体的にはSubmitのformは

<html:form method="post" action="/double" >
  <html:submit />
</html:form>

としているだけなのですが出力されるhtmlは

<form id="hogeForm" method="post" action="/strutsExamples/double.do">
  <div><input type="hidden" name="org.apache.struts.taglib.html.TOKEN" 
         value="612326e14b3ce599284543e2246f170b" /></div>
  <input type="submit" value="Submit" />
</form>

となります。あるキー値でhiddenにトークンがセットされています*3

次のリクエストで、サーバでトークンのチェック

さてこれでsessionにトークンがセットされ、さらにhtmlのhiddenにトークンがセットされました。次のリクエストを受けるアクション(DoubleAction?)では

synchronized (session) {
    tokenValid = isTokenValid(request);
    saveToken(request);
}
if (!tokenValid) {
    StringBuffer buffer = new StringBuffer();
    buffer.append("tokenチェックエラー");
    errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
            new String(buffer), false));
    saveErrors(request, errors);
}

となっています。

isTokenValid(request);

がSessionのトークンとhiddenのトークンをチェックするメソッドです。で、次の

saveToken(request);

で再度トークンを書き換えています。

isTokenValid?(request)はスレッドセーフですが、トークンを変更するまでスレッドセーフでなくてはいけないので、sessionインスタンスのモニタを取得して処理しています*4

Formでなく<html:link />の場合

上の例のように<html:form />タグの場合は自動でhiddenタグが挿入されトークンがPostされましたが、<html:link />タグなどの場合は

<html:link action="/double" transaction="true">リンク</html:link>

としてtransaction属性をtrueに指定します。transaction属性を指定すると

http://localhost:8080/strutsExamples/double.do
  ?org.apache.struts.taglib.html.TOKEN=f8e8901ab4cc126a148fe3d46d5596e7

とパラメタにトークンが設定されます。ちなみにこのtransaction属性はデフォルトはfalseなので、トークンを送信したい場合は明示的に指定してあげる必要があります。

サンプル。

この記事は

選択肢 投票
おもしろかった 237  
そうでもない 34  
  • その他のリンク系のタグは、rewriteタグがありますね。これもtransaction="true"をつけて制御します。 -- きの? 2008-01-27 (日) 00:39:44
  • メイン画面と別にサブウィンドウでも更新する画面の場合ってトークンがずれてしまいますよね? -- yama? 2011-03-02 (水) 13:07:06
  • auztxyMoZWaQ -- dzpntfpgxt? 2012-07-16 (月) 10:44:19
  • test1 -- test? 2015-07-15 (水) 09:11:21
  • 初めまして。下記の場合はどのようにすれば良いのでしょうか?フォームから送信した人に、トークンで期間限定のリンクを自動生成して返信、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにしたいのですが、 -- masaya? 2016-09-20 (火) 04:58:48
  • 初めまして。フォームから送信した人に、期間限定のリンクを自動生成して送信し、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにする場合はどのようにすれば良いでしょうか? -- masa? 2016-09-20 (火) 05:04:49
  • 初めまして。フォームから送信した人に、期間限定のリンクを自動生成して送信し、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにする場合はどのようにすれば良いでしょうか? -- masa? 2016-09-20 (火) 05:06:35
  • 初めまして。下記の場合はどのようにすれば良いのでしょうか?フォームから送信した人に、トークンで期間限定のリンクを自動生成して返信、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにしたいのですが、 -- masaya? 2016-09-20 (火) 05:08:31
  • 初めまして。フォームから送信した人に、期間限定のリンクを自動生成して送信し、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにする場合はどのようにすれば良いでしょうか? -- masa? 2016-12-18 (日) 16:53:53
  • 初めまして。フォームから送信した人に、期間限定のリンクを自動生成して送信し、期間内であれば指定したページが見れて、期間外であれば404ページが表示される、というようにする場合はどのようにすれば良いでしょうか? -- masa? 2016-12-18 (日) 16:54:04
  • ds -- 2017-02-22 (水) 14:59:59
  • ds -- ddd? 2017-02-22 (水) 15:00:15

Top / Struts / 同期トークンで二度押しなどをチェックする

現在のアクセス:107831


*1 中ではsessionIdをMD5でハッシュして、更に現在時刻でハッシュしているみたいです
*2 Sessionにセットされるときのキー値は"org.apache.struts.action.TOKEN"です。この定数値はorg.apache.struts.Globals.TRANSACTION_TOKEN_KEYで取得可能です。
*3 hiddenの定数値"org.apache.struts.taglib.html.TOKEN" はorg.apache.struts.Globals.TOKEN_KEYで取得可能です。
*4 スレッドセーフなメソッドを用意しておいてくれてもいいような、、、

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-10-31 (火) 10:44:09 (19d)