Spring Security 4和PrimeFaces 5 AJAX请求处理

dov*_*iin 2 jsf spring-security primefaces

美好的一天。

我已经创建了一个使用Spring Security 4的PrimeFaces 5项目(JSF 2.2)。我试图使用启用了单选的p:dataTable控件,该控件通过ajax调用来更新ap:pickList控件。

问题与Spring Security有关。如果我停用页面控件所在页面的安全性(admin.faces),则ajax行为可以正常工作。但是,如果我激活安全性,则会得到403状态代码,而pickList不会得到更新。我必须在此处指出,在激活安全性的情况下,如果我尝试进入管理页面而不先登录,则将我重定向到登录页面。

这是用于Spring Security的配置。为了简单起见,删除了一些代码:

<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>
Run Code Online (Sandbox Code Playgroud)

登录页面:

<!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>
Run Code Online (Sandbox Code Playgroud)

受保护的页面(管理员):

<!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>
Run Code Online (Sandbox Code Playgroud)

admin.faces中使用的javascript文件:

$(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);
    })
});
Run Code Online (Sandbox Code Playgroud)

感谢您的关注。

小智 5

对于AJAX调用,我也遇到了403 Responses的问题。问题是,没有CSRF令牌被提交。

通过手动添加:

<h:form>
    ...
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</h:form>
Run Code Online (Sandbox Code Playgroud)

有效。