2
votes

Good Day.

I've created a PrimeFaces 5 project (JSF 2.2) which makes use of Spring Security 4. I'm attempting to make use of p:dataTable control with single selection enabled, which through an ajax call, updates a p:pickList control.

The problem is related with Spring Security. If I deactivate the security of the page where my page controls are located (admin.faces), the ajax behavior works fine. But if I activate security, I get 403 status codes and the pickList doesn't get updated. I must indicate here that with security activated, if I attempt going to admin page, without logging first, I'm redirected to the login page.

This is the configuration used for Spring Security. Several code was eliminated for sake of simplicity:

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
                    http://www.springframework.org/schema/security 
                    http://www.springframework.org/schema/security/spring-security.xsd">

    <http pattern="/*.css" security="none" />
    <http pattern="/*.js" security="none" />

    <http use-expressions="true">
        <intercept-url pattern="/login.faces" access="permitAll" />
        <intercept-url pattern="/javax.faces.resource/**" access="permitAll"/>
        <intercept-url pattern="/admin.faces" access="hasRole('Administrator')" />

        <form-login
            login-page="/login.faces"
            authentication-failure-url="/login.faces" />
        <logout />
    </http>

    <authentication-manager alias="authManager">
        <authentication-provider ref="daoAuthenticationProvider"/>
    </authentication-manager>
</beans:beans>

The login page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui">

<h:head>
    <title>Reports</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="_csrf" content="#{_csrf.token}"/>
    <meta name="_csrf_header" content="#{_csrf.headerName}"/>
    <link rel="stylesheet" type="text/css" href="stylesheet.css" />
</h:head>
<body>
    <h:messages />
    <h:form id="loginForm">
        <input type="hidden" name="#{_csrf.parameterName}" value="#{_csrf.token}"/>

        <p:panelGrid columns="2">
            <h:outputLabel value="User:" />
            <h:inputText value="#{loginBean.user}" required="true"/>

            <h:outputLabel value="Password:" />
            <h:inputSecret value="#{loginBean.password}" required="true"/>

            <f:facet name="footer">
                <div style="text-align:right;">
                    <h:commandButton type="submit" id="login"
                        action="#{loginBean.login}" value="Login" />
                </div>
            </f:facet>
        </p:panelGrid>
    </h:form>
</body>
</html>

The protected page (admin):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">

<h:head>
    <title>Reports</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="_csrf" content="#{_csrf.token}"/>
    <meta name="_csrf_header" content="#{_csrf.headerName}"/>
    <link rel="stylesheet" type="text/css" href="stylesheet.css" />
    <h:outputScript library="primefaces" name="jquery/jquery.js"/>
    <h:outputScript library="js" name="admin.js"/>
</h:head>
<body>
    <h1>Reports</h1>
    <br />

    <div>
        <h:form id="form">
            <input type="hidden" name="#{_csrf.parameterName}" value="#{_csrf.token}"/>

            <p:messages id="messages" showDetail="false" showSummary="true"
                autoUpdate="true" closable="true" />
            <br />

            <div style="float: left; width: 25%; margin-right: 10px;">
                <p:dataTable value="#{reports.tables}" var="tbl"
                    selection="#{reports.tablesel}" selectionMode="single"
                    rowKey="#{tbl}" scrollable="true" scrollHeight="300" id="tables">
                    <p:ajax event="rowSelect" update=":form:selColumns" />

                    <p:column>
                        <f:facet name="header">
                            <h:outputText value="Tables" />
                        </f:facet>
                        <h:outputText value="#{tbl}" />
                    </p:column>
                </p:dataTable>
            </div>
            <div style="float: left;">
                <div style="margin-bottom: 10px;">
                    <div style="float: left; margin-right: 10px;">
                        <p:selectOneMenu value="#{reports.format}">
                            <f:selectItem itemValue="pdf" itemLabel="PDF" />
                            <f:selectItem itemValue="xls" itemLabel="Excel 2003" />
                        </p:selectOneMenu>
                    </div>
                    <div style="float: left; margin-right: 10px;">
                        <p:commandButton action="#{reports.create}"
                            value="View report" />
                    </div>
                    <div style="clear: both;"></div>
                </div>

                <p:pickList value="#{reports.lstColumns}" var="c"
                    itemLabel="#{c}" itemValue="#{c}" style="margin-bottom:10px;"
                    id="selColumns" />

                <div style="margin-bottom: 10px;">
                    <p:outputLabel value="Conditions" for="filter"
                        style="display:block;" />
                    <p:inputTextarea id="filter" value="#{reports.filter}"
                        style="width:97%;" />
                </div>

            </div>
        </h:form>
    </div>
</body>
</html>

A javascript file used in admin.faces:

$(document).ready(function() {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    })
});

Thanks for your attention.

1
You're not terribly clear on describing the actual problem. Did I understood correctly that every PrimeFaces-triggered ajax request returned a HTTP 403 response? Why exactly? Unauthorized URL? Or unauthorized headers? Or unauthorized session? etc.BalusC
Examining the ajax requests in Chrome, every time an AJAX request is sent to admin.faces, is received HTTP 403 forbidden as a response. I understand that means I'm attempting to access a protected resource, which requires I login with an authorized user (the required role is Administrator, as indicated in spring security config). This situation is rare, due using an Administrator user, I can access admin.faces with a normal request, but I cannot make ajax calls to the same page.dovahkiin

1 Answers

6
votes

I had the problem with 403 Responses for AJAX calls as well. The Problem was, that no CSRF Token was submitted.

By manually adding:

<h:form>
    ...
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</h:form>

it worked.