如何在不导致 CSRF 异常的情况下在 Alfresco 5 中正确实施 POST 网络脚本?
How do I properly implement a POST webscript in Alfresco 5 without causing a CSRF exception?
我创建了一个与 Alfresco 5 Community Edition 一起使用的模块扩展。一切正常,我的 .get 方法得到妥善处理,它们显示正确。
在页面上我有一个按钮,post返回服务器以显示结果(现在它是空的用于测试)。当我点击按钮时,出现服务器错误:
javax.servlet.ServletException: Possible CSRF attack noted when comparing token in session and request header. Request: POST /share/service/components/console/reset-dashboards
at org.alfresco.web.site.servlet.CSRFFilter$AssertTokenAction.run(CSRFFilter.java:827)
at org.alfresco.web.site.servlet.CSRFFilter.doFilter(CSRFFilter.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2466)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2455)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
检查 http://localhost:8080/share/page/index/uri/components/console/reset-dashboards
处的网络脚本 uri 我可以看到一切都已正确注册
Reset Dashboards
GET /share/page/components/console/reset-dashboards
Description: Dashboard Tools - Reset All Dashboards To Default
Authentication: none
Transaction: none
Format Style: any
Default Format: html
Id: com/company/components/console/dashboard-tools/reset-dashboards.get
Description: classpath:alfresco/site-webscripts/com/company/components/console/dashboard-tools/reset-dashboards.get.desc.xml
Reset Dashboards POST
POST /share/page/components/console/reset-dashboards
Description: Dashboard Tools - Reset All Dashboards To Default POST
Authentication: user
Transaction: required
Format Style: any
Default Format: json
Id: com/company/components/console/dashboard-tools/reset-dashboards.post
Description: classpath:alfresco/site-webscripts/com/company/components/console/dashboard-tools/reset-dashboards.post.desc.xml
这是 GET webscript 的信息:
Script Properties
Id: com/company/components/console/dashboard-tools/reset-dashboards.get
Short Name: Reset Dashboards
Description: Dashboard Tools - Reset All Dashboards To Default
Authentication: none
Transaction: none
Method: GET
URL Template: /components/console/reset-dashboards
Format Style: any
Default Format: html
Negotiated Formats: [undefined]
Implementation: class org.springframework.extensions.webscripts.DeclarativeWebScript
Extensions: [undefined]
Store: classpath:alfresco/site-webscripts
File: com/company/components/console/dashboard-tools/reset-dashboards.get.desc.xml
<webscript>
<shortname>Reset Dashboards</shortname>
<description>Dashboard Tools - Reset All Dashboards To Default</description>
<url>/components/console/reset-dashboards</url>
<family>admin-console</family>
</webscript>
File: com/company/components/console/dashboard-tools/reset-dashboards.get.html.ftl
<@markup id="css" >
<#-- CSS Dependencies -->
<@link href="${url.context}/res/components/console/application.css" group="console"/>
</@>
<@markup id="js">
<#-- JavaScript Dependencies -->
<@script src="${url.context}/res/components/console/consoletool.js" group="console"/>
<#--<@script src="${url.context}/res/components/console/application.js" group="console"/>-->
</@>
<@markup id="widgets">
<@createWidgets group="console"/>
</@>
<@markup id="html">
<#assign el=args.htmlid?html>
<form id="${el}-options-form" action="${url.context}/service/components/console/reset-dashboards" method="post">
<div class="buttons">
<button id="${el}-apply-button" name="apply">${msg("button.reset")}</button>
</div>
</form>
</@>
File: com/company/components/console/dashboard-tools/reset-dashboards.get.js
function main()
{
}
main();
Store: classpath:surf/webscripts
[No implementation files]
Store: classpath:webscripts
[No implementation files]
这是 POST 网络脚本的信息:
Script Properties
Id: com/company/components/console/dashboard-tools/reset-dashboards.post
Short Name: Reset Dashboards POST
Description: Dashboard Tools - Reset All Dashboards To Default POST
Authentication: user
Transaction: required
Method: POST
URL Template: /components/console/reset-dashboards
Format Style: any
Default Format: json
Negotiated Formats: [undefined]
Implementation: class org.springframework.extensions.webscripts.DeclarativeWebScript
Extensions: [undefined]
Store: classpath:alfresco/site-webscripts
File: com/company/components/console/dashboard-tools/reset-dashboards.post.desc.xml
<webscript>
<shortname>Reset Dashboards POST</shortname>
<description>Dashboard Tools - Reset All Dashboards To Default POST</description>
<format default="json" />
<url>/components/console/reset-dashboards</url>
<authentication>user</authentication>
</webscript>
File: com/company/components/console/dashboard-tools/reset-dashboards.post.json.ftl
{
"success": ${success?string},
"message": "<#if errormsg??>${errormsg}</#if>"
}
File: com/company/components/console/dashboard-tools/reset-dashboards.post.json.js
function main()
{
model.success = true;
}
main();
Store: classpath:surf/webscripts
[No implementation files]
Store: classpath:webscripts
[No implementation files]
同样,当我点击页面上的按钮 post 到 webscript 时,它给了我一个 Possible CSRF attack
异常。我该如何纠正这个问题?是否可以在模块扩展 jar
文件中执行此操作?
更新
我确实注意到 post 作为 Alfresco 的一部分的所有其他脚本都包含 Alfresco-CSRFToken
header 和同名的 cookie。我的脚本仅包含 Alfresco-CSRFToken
作为 cookie,并且缺少 header。不过,我不确定如何确保它作为 header 存在。
如果您在 alfresco 共享客户端脚本中调用存储库网络脚本,那么您需要注意以下几点才能通过 CSRF 过滤器。
尝试在发送请求时使用标准 Alfresco.util.Ajax、alfresco/core/CoreXhr 或 Alfresco.forms.Form。
如果您没有使用以上任何一项,那么您需要进行与 CSRF 令牌相关的额外检查,如下所示。
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
{
xhrHeadersObject[Alfresco.util.CSRFPolicy.getHeader()] = Alfresco.util.CSRFPolicy.getToken();
}
如果是 YUI 数据源。
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
{
yuiDataSource.connMgr.initHeader(Alfresco.util.CSRFPolicy.getHeader(), Alfresco.util.CSRFPolicy.getToken(), false);
}
通过使用上述方法,您可以消除代码中与 CSRF 攻击相关的错误。
我创建了一个与 Alfresco 5 Community Edition 一起使用的模块扩展。一切正常,我的 .get 方法得到妥善处理,它们显示正确。
在页面上我有一个按钮,post返回服务器以显示结果(现在它是空的用于测试)。当我点击按钮时,出现服务器错误:
javax.servlet.ServletException: Possible CSRF attack noted when comparing token in session and request header. Request: POST /share/service/components/console/reset-dashboards
at org.alfresco.web.site.servlet.CSRFFilter$AssertTokenAction.run(CSRFFilter.java:827)
at org.alfresco.web.site.servlet.CSRFFilter.doFilter(CSRFFilter.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2466)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2455)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
检查 http://localhost:8080/share/page/index/uri/components/console/reset-dashboards
处的网络脚本 uri 我可以看到一切都已正确注册
Reset Dashboards
GET /share/page/components/console/reset-dashboards
Description: Dashboard Tools - Reset All Dashboards To Default
Authentication: none
Transaction: none
Format Style: any
Default Format: html
Id: com/company/components/console/dashboard-tools/reset-dashboards.get
Description: classpath:alfresco/site-webscripts/com/company/components/console/dashboard-tools/reset-dashboards.get.desc.xml
Reset Dashboards POST
POST /share/page/components/console/reset-dashboards
Description: Dashboard Tools - Reset All Dashboards To Default POST
Authentication: user
Transaction: required
Format Style: any
Default Format: json
Id: com/company/components/console/dashboard-tools/reset-dashboards.post
Description: classpath:alfresco/site-webscripts/com/company/components/console/dashboard-tools/reset-dashboards.post.desc.xml
这是 GET webscript 的信息:
Script Properties
Id: com/company/components/console/dashboard-tools/reset-dashboards.get
Short Name: Reset Dashboards
Description: Dashboard Tools - Reset All Dashboards To Default
Authentication: none
Transaction: none
Method: GET
URL Template: /components/console/reset-dashboards
Format Style: any
Default Format: html
Negotiated Formats: [undefined]
Implementation: class org.springframework.extensions.webscripts.DeclarativeWebScript
Extensions: [undefined]
Store: classpath:alfresco/site-webscripts
File: com/company/components/console/dashboard-tools/reset-dashboards.get.desc.xml
<webscript>
<shortname>Reset Dashboards</shortname>
<description>Dashboard Tools - Reset All Dashboards To Default</description>
<url>/components/console/reset-dashboards</url>
<family>admin-console</family>
</webscript>
File: com/company/components/console/dashboard-tools/reset-dashboards.get.html.ftl
<@markup id="css" >
<#-- CSS Dependencies -->
<@link href="${url.context}/res/components/console/application.css" group="console"/>
</@>
<@markup id="js">
<#-- JavaScript Dependencies -->
<@script src="${url.context}/res/components/console/consoletool.js" group="console"/>
<#--<@script src="${url.context}/res/components/console/application.js" group="console"/>-->
</@>
<@markup id="widgets">
<@createWidgets group="console"/>
</@>
<@markup id="html">
<#assign el=args.htmlid?html>
<form id="${el}-options-form" action="${url.context}/service/components/console/reset-dashboards" method="post">
<div class="buttons">
<button id="${el}-apply-button" name="apply">${msg("button.reset")}</button>
</div>
</form>
</@>
File: com/company/components/console/dashboard-tools/reset-dashboards.get.js
function main()
{
}
main();
Store: classpath:surf/webscripts
[No implementation files]
Store: classpath:webscripts
[No implementation files]
这是 POST 网络脚本的信息:
Script Properties
Id: com/company/components/console/dashboard-tools/reset-dashboards.post
Short Name: Reset Dashboards POST
Description: Dashboard Tools - Reset All Dashboards To Default POST
Authentication: user
Transaction: required
Method: POST
URL Template: /components/console/reset-dashboards
Format Style: any
Default Format: json
Negotiated Formats: [undefined]
Implementation: class org.springframework.extensions.webscripts.DeclarativeWebScript
Extensions: [undefined]
Store: classpath:alfresco/site-webscripts
File: com/company/components/console/dashboard-tools/reset-dashboards.post.desc.xml
<webscript>
<shortname>Reset Dashboards POST</shortname>
<description>Dashboard Tools - Reset All Dashboards To Default POST</description>
<format default="json" />
<url>/components/console/reset-dashboards</url>
<authentication>user</authentication>
</webscript>
File: com/company/components/console/dashboard-tools/reset-dashboards.post.json.ftl
{
"success": ${success?string},
"message": "<#if errormsg??>${errormsg}</#if>"
}
File: com/company/components/console/dashboard-tools/reset-dashboards.post.json.js
function main()
{
model.success = true;
}
main();
Store: classpath:surf/webscripts
[No implementation files]
Store: classpath:webscripts
[No implementation files]
同样,当我点击页面上的按钮 post 到 webscript 时,它给了我一个 Possible CSRF attack
异常。我该如何纠正这个问题?是否可以在模块扩展 jar
文件中执行此操作?
更新
我确实注意到 post 作为 Alfresco 的一部分的所有其他脚本都包含 Alfresco-CSRFToken
header 和同名的 cookie。我的脚本仅包含 Alfresco-CSRFToken
作为 cookie,并且缺少 header。不过,我不确定如何确保它作为 header 存在。
如果您在 alfresco 共享客户端脚本中调用存储库网络脚本,那么您需要注意以下几点才能通过 CSRF 过滤器。
尝试在发送请求时使用标准 Alfresco.util.Ajax、alfresco/core/CoreXhr 或 Alfresco.forms.Form。
如果您没有使用以上任何一项,那么您需要进行与 CSRF 令牌相关的额外检查,如下所示。
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
{
xhrHeadersObject[Alfresco.util.CSRFPolicy.getHeader()] = Alfresco.util.CSRFPolicy.getToken();
}
如果是 YUI 数据源。
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
{
yuiDataSource.connMgr.initHeader(Alfresco.util.CSRFPolicy.getHeader(), Alfresco.util.CSRFPolicy.getToken(), false);
}
通过使用上述方法,您可以消除代码中与 CSRF 攻击相关的错误。