Primefaces DialogFramework - Как показать диалог, расположенный в WEB-INF?

Я использую DialogFramework с помощью Primefaces

  • Primefaces 5.0
  • Мохарра 2.1.27
  • Glassfish 3.1.2.2 Сборка 5

Моя проблема в том, что если пользователь знает расположение моего диалога, он может получить к нему доступ напрямую через URL-адрес. Я не хочу, чтобы это было возможно, поэтому я подумал, что можно будет поместить диалог в папку WEB-INF моего веб-приложения, но теперь, если я хочу открыть диалог, я получаю FileNotFound-Exception.

Если мой диалог находится в какой-то обычной папке, он отлично работает

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, или это вообще невозможно? заранее спасибо


person stg    schedule 25.07.2014    source источник


Ответы (1)


К сожалению, размещение диалогов PrimeFaces Dialog Framework в /WEB-INF, чтобы предотвратить прямой доступ, действительно не будет работать. Диалоги загружаются полностью на стороне клиента. По запросу POST, который открывает диалоговое окно, JSF / PrimeFaces возвращает сценарий oncomplete с (общедоступным!) URL-адресом диалогового окна в JavaScript / jQuery, который, в свою очередь, показывает базовый шаблон диалогового окна с <iframe>, URL-адрес которого установлен на URL-адрес диалогового окна. , который, в свою очередь, загружает контент. Фактически отправляются 2 запроса: первый для получения URL-адреса диалогового окна, а второй для получения содержимого диалогового окна на основе этого URL-адреса в <iframe>.

Невозможно сохранить диалог в /WEB-INF без возврата к «традиционному» подходу к диалогу через <p:dialog> и условному отображению через JS / CSS. На стороне сервера также нет возможности проверить на основе некоторых заголовков, поступает ли запрос от <iframe>, так что все остальные могут быть просто заблокированы. Ближайшая ваша ставка - это заголовок referer, но его можно подделать.

Один из способов минимизировать злоупотребления - проверить наличие параметра запроса pfdlgcid (обозначенного _ 10_) при запросе диалогового окна. PrimeFaces, в частности, добавляет этот параметр запроса, представляющий «идентификатор разговора», к URL-адресу диалога. Предполагая, что все диалоги хранятся в папке /dialogs, вы можете выполнить эту работу с помощью простого фильтра сервлетов. Вот начальный пример, который отправляет ошибку HTTP 400, когда /dialogs/* запрашивается без параметра запроса pfdlgcid.

@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 в карте сеанса, которая, в свою очередь, также сравнивается в фильтре сервлета.

Сначала добавьте в 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 исходный код в свой пакет и добавьте следующую строку после строки 62:

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

Замени <navigation-handler> в faces-config.xml на настроенный.

Наконец, измените условие if в методе DialogFilter#doFilter() следующим образом:

if (getIds(request).contains(id)) {
    // ...
}

Теперь это предотвращает попытку злоумышленника открыть диалог со случайным идентификатором. Однако это не мешает злоумышленнику попытаться открыть диалоговое окно, скопировав точный <iframe> URL-адрес сразу после его открытия. Учитывая то, как работает диалоговая структура PrimeFaces, предотвратить это невозможно. Вы можете в лучшем случае удалить значение pfdlgcid из сеанса, когда диалог вот-вот вернется к родительскому. Однако, когда диалог закрывается чистыми средствами JS, это также игнорируется.

В общем, если вы действительно, действительно, не хотите, чтобы конечный пользователь мог открывать диалоговое окно индивидуально, тогда вы не можете обойти "традиционный" <p:dialog> подход.

person BalusC    schedule 27.07.2014