Ответ 1
Этот подход переопределения рендерера <h:form>
небезопасен для PrimeFaces partialSubmit="true"
. Кроме того, повторное использование скрытого поля, идентифицирующего представленную форму, будет специфичным для JSF-реализации, поскольку это не является частью JSF API.
С другой стороны, гораздо проще хранить токен CSRF непосредственно непосредственно в состоянии представления JSF. Вы можете добиться этого с помощью настраиваемого ViewHandler
, как показано ниже, который устанавливает атрибут в UIViewRoot
(который автоматически сохраняется в состоянии представления JSF):
public class CsrfViewHandler extends ViewHandlerWrapper {
private static final String CSRF_TOKEN_KEY = CsrfViewHandler.class.getName();
private ViewHandler wrapped;
public CsrfViewHandler(ViewHandler wrapped) {
this.wrapped = wrapped;
}
@Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
UIViewRoot view = super.restoreView(context, viewId);
return getCsrfToken(context).equals(view.getAttributes().get(CSRF_TOKEN_KEY)) ? view : null;
}
@Override
public void renderView(FacesContext context, UIViewRoot view) throws IOException, FacesException {
view.getAttributes().put(CSRF_TOKEN_KEY, getCsrfToken(context));
super.renderView(context, view);
}
private String getCsrfToken(FacesContext context) {
String csrfToken = (String) context.getExternalContext().getSessionMap().get(CSRF_TOKEN_KEY);
if (csrfToken == null) {
csrfToken = UUID.randomUUID().toString();
context.getExternalContext().getSessionMap().put(CSRF_TOKEN_KEY, csrfToken);
}
return csrfToken;
}
@Override
public ViewHandler getWrapped() {
return wrapped;
}
}
Обратите внимание, что когда restoreView()
возвращает null
, JSF будет бросать ViewExpiredException
"как обычно".
Чтобы запустить его, зарегистрируйтесь как ниже в faces-config.xml
:
<application>
<view-handler>com.example.CsrfViewHandler</view-handler>
</application>
Поскольку он не имеет дополнительного значения при сохранении состояния на стороне сервера, при необходимости можно определить, как показано ниже в конструкторе обработчика вида, если текущее приложение JSF настроено с сохранением состояния на стороне клиента:
FacesContext context = FacesContext.getCurrentInstance();
if (!context.getApplication().getStateManager().isSavingStateInClient(context)) {
throw new IllegalStateException("This view handler is only applicable when JSF is configured with "
+ StateManager.STATE_SAVING_METHOD_PARAM_NAME + "=" + StateManager.STATE_SAVING_METHOD_CLIENT);
}