7

によって保護されていない JSF ページがありますj_security_check。次の手順を実行します。

  1. ブラウザーで JSF ページを開きます。
  2. サーバーを再起動します。
  3. JSF ページのコマンド ボタンをクリックして、ajax 呼び出しを開始します。

Firebug は、ViewExpiredException期待どおりに a が発生したことを示しています。

役職:

javax.faces.ViewState=8887124636062606698:-1513851009188353364

応答:

<partial-response>
<error>
<error-name>class javax.faces.application.ViewExpiredException</error-name>
<error-message>viewId:/viewer.xhtml - View /viewer.xhtml could not be restored.</error-message>
</error>
</partial-response>

ただし、ページを保護するように構成し、j_security_check上記と同じ手順を実行すると、奇妙なことに (私にとって)ViewExpiredExceptionは発生しなくなりました。代わりに、応答は単なる新しいビュー ステートです。

役職:

javax.faces.ViewState=-4873187770744721574:8069938124611303615

応答:

<partial-response>
<changes>
<update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update>
</changes>
</partial-response>

誰かがこれを理解するのを手伝ってくれますか? 例外が発生することを期待しているので、その例外を処理してエラーページを表示できます。今では、新しい ViewState で応答するだけで、私のページは視覚的なフィードバックなしで動かなくなりました。

4

2 に答える 2

13

問題を再現できました。ここで起こっていることはRequestDispatcher#forward()、セキュリティ制約で指定されているように、コンテナーがログイン ページに対して a を呼び出すことです。ただし、ログイン ページ自体が JSF ページでもある場合はFacesServlet、転送されたリクエストで も呼び出されます。リクエストは転送であるため、転送されたリソース (ログイン ページ) に新しいビューが作成されるだけです。ただし、これは ajax リクエストであり、render情報がないため (POST リクエスト全体は基本的にセキュリティ チェック フォワード中に破棄されます)、ビュー ステートのみが返されます。

ログインページが JSF ページ (JSP やプレーン HTML など) でない場合、ajax リクエストはページの HTML 出力全体を ajax レスポンスとして返しますが、これは JSF ajax では解析できず、「空の」レスポンスとして解釈されます。

残念ながら、「設計どおり」に機能しています。ajax リクエストのセキュリティ制約チェックに関して、JSF 仕様に見落としがあるのではないかと思います。結局、原因は理解でき、幸いなことに解決は簡単です。ただし、実際にはここでエラー ページを表示するのではなく、非 ajax リクエストの場合とまったく同じように、ログイン ページ全体を表示する必要があります。現在のリクエストが ajax リクエストであり、ログイン ページに転送されているかどうかを確認するだけで、ビュー全体が変更されるように特別な「リダイレクト」ajax レスポンスを送信する必要があります。

PhaseListener次のように a を使用してこれを実現できます。

public class AjaxLoginListener implements PhaseListener {

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        // NOOP.
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
        String loginURL = request.getContextPath() + "/login.xhtml";

        if (context.getPartialViewContext().isAjaxRequest()
            && originalURL != null
            && loginURL.equals(request.getRequestURI()))
        {
            try {
                context.getExternalContext().invalidateSession();
                context.getExternalContext().redirect(originalURL);
            } catch (IOException e) {
                throw new FacesException(e);
            }
        }
    }
}

このソリューションの更新は、 OmniFaces 1.2が に組み込まれたためOmniPartialViewContextです。したがって、既に OmniFaces を使用している場合、この問題は完全に透過的に解決され、カスタムは必要ありませんPhaseListener

于 2012-09-20T03:29:34.077 に答える