1

JSFカスタムタグとしてキャプチャコンポーネントをビルドしたアプリケーションで問題が発生しました。

私が使用しているJavaEE6Webアプリケーションでは、JSF 2.1 + Jboss Richfaces 4.2.3 + EJB 3.1 + JPA 2.0 + PrettyFaces 3.3.3

次のJSF2カスタムタグがあります。

<tag>
    <tag-name>captcha</tag-name>
    <source>tags/captcha.xhtml</source>
</tag>  

accountEdit.xhtmlという名前のXHTMLページに、キャプチャが表示されています。

                <ui:fragment rendered="#{customerMB.screenComponent.pageName eq 'create'}">
                    <div class="form_row">
                            <label class="contact"><strong>#{msg.captcha}:</strong>
                            </label>
                            <atl:captcha></atl:captcha>                     
                    </div>                                                          
                </ui:fragment>

captcha.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich">

    <table border="0">
        <tr>
            <td>
            <h:graphicImage id="capImg" value="#{facesContext.externalContext.requestContextPath}/../captcha.jpg" />
            </td>
            <td><a4j:commandButton id="resetCaptcha" value="#{msg.changeImage}" immediate="true" action="#{userMB.resetCaptcha}" >
                <a4j:ajax render="capImg" execute="@this" />                
            </a4j:commandButton></td>
        </tr>
        <tr>
            <td><h:inputText value="#{userMB.captchaComponent.captchaInputText}" /></td>            
        </tr>
    </table>

</ui:composition>

私のweb.xmlで、実行時にキャプチャを生成する要求を処理するCaptchaServletを構成しました。

<servlet>   
    <servlet-name>CaptchaServlet</servlet-name>
    <servlet-class>com.myapp.web.common.servlet.CaptchaServlet</servlet-class>      
    <init-param>
        <description>passing height</description>
        <param-name>height</param-name>
        <param-value>30</param-value>
    </init-param>
    <init-param>
        <description>passing width</description>
        <param-name>width</param-name>
        <param-value>120</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>


<servlet-mapping>
    <servlet-name>CaptchaServlet</servlet-name>
    <url-pattern>/captcha.jpg</url-pattern>
</servlet-mapping>

私のCaptchaServletの実装:

public class CaptchaServlet extends HttpServlet {

    /**
     * 
     */
    private static final long serialVersionUID = 6105436133454099605L;

    private int height = 0;
    private int width = 0;
    public static final String CAPTCHA_KEY = "captcha_key_name";

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        height = Integer
                .parseInt(getServletConfig().getInitParameter("height"));
        width = Integer.parseInt(getServletConfig().getInitParameter("width"));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse response)
            throws IOException, ServletException {

        // Expire response
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Max-Age", 0);

        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2D = image.createGraphics();
        Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
        Random r = new Random();
        String token = Long.toString(Math.abs(r.nextLong()), 36);
        String ch = token.substring(0, 6);
        Color c = new Color(0.6662f, 0.4569f, 0.3232f);
        GradientPaint gp = new GradientPaint(30, 30, c, 15, 25, Color.white,
                true);
        graphics2D.setPaint(gp);
        Font font = new Font("Verdana", Font.CENTER_BASELINE, 26);
        graphics2D.setFont(font);
        graphics2D.drawString(ch, 2, 20);
        graphics2D.dispose();
        HttpSession session = req.getSession(true);
        session.setAttribute(CAPTCHA_KEY, ch);

        OutputStream outputStream = response.getOutputStream();
        ImageIO.write(image, "jpeg", outputStream);
        outputStream.close();
    }
}

Glassfish 3.1.1でこのアプリを実行すると、レンダリング中にサーブレットのdoGet()メソッドが呼び出されます

レンダリングするHttpServletdoGet()メソッドの場合:

<h:graphicImage id="capImg" value="#{facesContext.externalContext.requestContextPath}/../captcha.jpg" />

doGet()はGoogle Chromeに対して1回だけレンダリングされるため、正しくレンダリングされます。

FirefoxおよびIEの場合、doGet()はキャプチャキーを2回更新しますが、ページにペイントされたキャプチャ画像は更新しません。

誰かがこれを修正する方法と、Chromeで他のブラウザとは異なる動作をする理由を知っている人がいたら教えてください。

前もって感謝します!


ブラウザは応答をキャッシュしています。これを回避する試みは不完全で正しくありません。

response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Max-Age", 0);

すべてのブラウザでWebページのキャッシュを制御する方法を参照してください。適切なセットの場合:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.

さらに、堅牢にするために、現在のタイムスタンプがミリ秒単位のクエリ文字列を画像のURLに追加します。これは、次java.util.Dateの名前のマネージドBeanとしてインスタンスがある場合の例nowです。

<h:graphicImage id="capImg" value="#{request.contextPath}/../captcha.jpg?#{now.time}" />

(リクエストコンテキストパスを取得する方法も簡略化したことに注意してください。とにかくドメインルートに移動した場合にどのように役立つかはわかりませ../ん)

4

2 に答える 2

3

ブラウザは応答をキャッシュしています。これを回避しようとするあなたの試みは不完全であり、正しくありません:

response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Max-Age", 0);

すべてのブラウザーで Web ページのキャッシュを制御する方法を参照してください。適切なセットの場合:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.

さらに、より堅牢にするために、ミリ秒単位の現在のタイムスタンプを含むクエリ文字列を画像 URL に追加します。java.util.Dateという名前のマネージド Bean としてインスタンスがある場合の例を次に示しますnow

<h:graphicImage id="capImg" value="#{request.contextPath}/../captcha.jpg?#{now.time}" />

(リクエストコンテキストパスを取得する方法も単純化したことに注意してください。とにかくドメインルートに移動した場合にどのように役立つかはわかりませ../ん)

于 2013-03-27T12:11:33.500 に答える
0

私はこれに対する解決策を見つけました、それは最適な解決策ではありませんが、それはうまくいきます、ここにそれは行きます:

captcha.xhtml

<table border="0">
    <tr>
        <td>
            <h:graphicImage url="#{request.contextPath}/../jcaptcha"/>
        </td>
        <td>
            <input type='text' name='j_captcha_response' value='' />
        </td>
    </tr>
</table>

CaptchaServlet doGetメソッド:

    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {

        byte[] captchaChallengeAsJpeg = null;
        // the output stream to render the captcha image as jpeg into
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
        // get the session id that will identify the generated captcha.
        //the same id must be used to validate the response, the session id is a good candidate!
        String captchaId = httpServletRequest.getSession().getId();
            // call the ImageCaptchaService getChallenge method
            BufferedImage challenge =
                    CaptchaServiceSingleton.getImageChallengeForID(captchaId,
                            httpServletRequest.getLocale());
            // a jpeg encoder
            JPEGImageEncoder jpegEncoder =
                    JPEGCodec.createJPEGEncoder(jpegOutputStream);
            jpegEncoder.encode(challenge);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        } catch (CaptchaServiceException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return;
        }
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();

        // flush it in the response
        httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream =
                httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

CaptchaServiceRequestSingleton.javaを作成しました

    package com.myapp.web.common.listener;

    import java.awt.image.BufferedImage;
    import java.util.HashMap;
    import java.util.Locale;

    import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
    import com.octo.captcha.service.image.ImageCaptchaService;

public class CaptchaServiceSingleton {

    private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();
    private static final int MAX_CACHE_SIZE = 200;
    private static HashMap<String, BufferedImage> captchaImgCache = new HashMap<String, BufferedImage>();

    public static ImageCaptchaService getInstance(){
        return instance;
    }

    public static BufferedImage getImageChallengeForID(String id, Locale locale) {
        if (captchaImgCache.containsKey(id)) {
            return captchaImgCache.get(id);
        } else {
            BufferedImage bImage = instance.getImageChallengeForID(id, locale);

            // if limit reached reset captcha cache
            if (captchaImgCache.size() > MAX_CACHE_SIZE) {
                captchaImgCache = new HashMap<String, BufferedImage>();
            }

            captchaImgCache.put(id, bImage);
            return bImage;
        }
    }

    public static void resetImageChallengeForID(String id) {        
        if (captchaImgCache.containsKey(id)) {      
            captchaImgCache.remove(id);
        }               
    }

}

[アカウントの作成]ボタンをクリックすると、キャプチャがリセットされます。

CustomerMB.openCreateCustomerAccount():

public String openCreateCustomerAccount() {
    customerAccountEditVO = new CustomerAccountVO();
    screenComponent.setPageName(NameConstants.CREATE);
    getUserMB().resetCaptcha();
    return null;
}

UserMB.resetCaptcha()の場合:

public String resetCaptcha() {
    CaptchaServiceSingleton.resetImageChallengeForID(JSFUtil.getRequest().getRequestedSessionId());     
    return null;
}

おそらくそれは完璧な解決策ではありませんが、少なくともすべてのブラウザで機能しています。

于 2013-03-29T22:25:04.003 に答える