Primefaces DialogFramework - 如何显示位于WEB-INF中的对话框?

时间:2014-07-25 16:28:26

标签: jsf-2 primefaces dialog web-inf

我正在使用Primefaces DialogFramework

  • Primefaces 5.0
  • Mojarra 2.1.27
  • Glassfish 3.1.2.2 Build 5

我的问题是,如果用户知道我的对话框的位置,他可以直接通过URL访问它。我不希望这是可能的,所以我认为它可以将对话框放在我的web-app的WEB-INF文件夹中,但是现在,如果我想打开对话框,我会得到一个FileNotFound-Exception。 / p>

如果我的对话框位于某个常规文件夹中,则可以正常使用

RequestContext.getCurrentInstance().openDialog("/myfolder/mydialog"); 
// this works as expected

但如果它位于WEB-INF中,则不再起作用

RequestContext.getCurrentInstance().openDialog("/WEB-INF/mydialog",options,null);
// this is causing a fileNotFoundException

我还试图在faces-config中为此设置导航规则,但又没有成功

<navigation-case>
    <from-outcome>mydialog</from-outcome>
    <to-view-id>/WEB-INF/mydialog.xhtml</to-view-id>
    <redirect />
</navigation-case>

如何打开位于WEB-INF文件夹中的对话框,或者根本不可能? 提前致谢

1 个答案:

答案 0 :(得分:8)

不幸的是,使用PrimeFaces对话框架对话框in /WEB-INF in order to prevent direct access确实不起作用。对话框完全在客户端加载。在打开对话框的POST请求中,JSF / PrimeFaces返回一个oncomplete脚本,其中包含对话框的(public!)URL到JavaScript / jQuery,后者又显示了一个带有<iframe>的基本对话框模板其URL设置为对话框URL,后者又加载内容。实际上,正在发送2个请求,第一个是获取对话框的URL,第二个是根据<iframe>中的URL获取对话框的内容。

没有办法在/WEB-INF中保持对话,而不会通过<p:dialog>回退到“传统”对话框方法,并通过JS / CSS进行条件显示。如果请求来自<iframe>,服务器端也无法根据某些标头进行验证,以便可以简单地阻止所有其他标头。你最接近的赌注是referer标题,但这可能是欺骗性的。

最小化滥用行为的一种方法是在请求对话时检查pfdlgcid请求参数(由Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM标识)的存在。 PrimeFaces即将表示“会话ID”的请求参数附加到对话URL。假设所有对话框都存储在文件夹/dialogs中,那么您可以使用简单的servlet filter完成工作。这是一个启动示例,在没有/dialogs/*请求参数的情况下请求pfdlgcid时发送HTTP 400错误。

@WebFilter("/dialogs/*")
public class DialogFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        String id = request.getParameter(Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM);

        if (id != null) {
            chain.doFilter(req, res); // Okay, just continue request.
        }
        else {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST); // 400 error.
        }
    }

    // ...
}

但是,滥用者可能不是那么愚蠢,并且在正常流程中发现pfdlgcid请求参数,并且仍然能够在提供该参数时单独打开对话框,即使是随机值也是如此。我想过将实际pfdlgcid值与已知值进行比较。我检查了PrimeFaces DialogNavigationHandler源代码,但不幸的是,PrimeFaces不会在会话中的任何位置存储此值。您需要提供自定义DialogNavigationHandler实现,其中将pfdlgcid值存储在会话映射中,而后者也会在servlet过滤器中进行比较。

首先将以下方法添加到DialogFilter

public static Set<String> getIds(HttpServletRequest request) {
    HttpSession session = request.getSession();
    Set<String> ids = (Set<String>) session.getAttribute(getClass().getName());

    if (ids == null) {
        ids = new HashSet<>();
        session.setAttribute(getClass().getName(), ids);
    }

    return ids;
}

然后将PrimeFaces DialogNavigationHandler source code复制到您自己的包中,并在第62行后添加以下行:

DialogFilter.getIds((HttpServletRequest) context.getExternalContext().getRequest()).add(pfdlgcid);

<navigation-handler>中的faces-config.xml替换为自定义的if

最后,更改DialogFilter#doFilter()方法中的if (getIds(request).contains(id)) { // ... } 条件,如下所示:

<iframe>

现在,这可以防止滥用者尝试使用随机ID打开对话框。但是,这并不会阻止滥用者通过在打开后立即复制确切的pfdlgcid URL来尝试打开对话框。鉴于PrimeFaces对话框架的工作方式,没有办法防止这种情况发生。当对话框即将返回父级时,您最多可以从会话中删除<p:dialog>值。但是,当纯JS意味着关闭对话框时,这也被绕过了。

总而言之,如果你真的,真的,想要避免最终用户能够单独打开对话框,那么你就无法绕过“传统的”{{1}}方法。