Struts2升级至6.1.1后Content-Security-Policy脚本加载报错
问题背景
将Struts2项目从struts2-core-2.5.30升级到struts2-core-6.1.1后,出现CSP安全策略违规错误,无法加载本地jQuery等脚本,错误提示如下:
[Report Only] Refused to load the script '' because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
具体无法加载的脚本示例:
[Report Only] Refused to load the script
<code>http://localhost:8080/Portal/html/js/jquery/jquery-1.8.3.min.js</code>because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
[Report Only] Refused to load the script
<code>http://localhost:8080/Portal/html/js/jquery/jquery-ui.1.10.4.min.js</code>because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
已尝试的无效方案
- 多种Meta标签配置:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'"> <meta http-equiv="Content-Security-Policy" content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *;"> <meta http-equiv="Content-Security-Policy" content="default-src 'nonce-rAnd0m'"> <script src="${pageContext.request.contextPath}/html/js/jquery/jquery-1.8.3.min.js" type="text/javascript" nonce="rAnd0m123"></script>
- 自定义Interceptor添加响应头:
import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.StrutsStatics; public class SessionInterceptor extends AbstractInterceptor{ private static final long serialVersionUID = 1L; public String intercept(ActionInvocation invocation) throws Exception { ActionContext ac = invocation.getInvocationContext(); HttpServletResponse response = (HttpServletResponse) ac.get(StrutsStatics.HTTP_RESPONSE); response.addHeader("X-Frame-Options", "SAMEORIGIN"); response.addHeader("Content-Security-Policy-Report-Only", "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'none'; frame-src 'none'; font-src 'self'; connect-src 'self'; report-uri REDACTED"); response.addHeader("X-Content-Security-Policy-Report-Only", "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'none'; frame-src 'none'; font-src 'self'; connect-src 'self'; report-uri REDACTED"); return invocation.invoke(); } }
- 升级jQuery版本(从1.8.3升级)
解决方案
核心原因
Struts2 6.x版本默认启用了内置的CSP拦截器,它会自动添加带有nonce和strict-dynamic的CSP头,手动添加的Meta标签或自定义拦截器的头会被覆盖或冲突——浏览器会合并所有CSP规则并取最严格限制。
具体解决步骤
- 禁用内置CSP拦截器(按需选择)
如果不需要默认CSP防护,直接在struts.xml中关闭内置拦截器:
<struts> <constant name="struts.security.csp.enabled" value="false" /> </struts>
- 通过Struts配置自定义CSP规则(推荐)
若需保留CSP防护,不要手动添加头,而是通过Struts配置修改规则:
<struts> <!-- 启用CSP(默认值为true) --> <constant name="struts.security.csp.enabled" value="true" /> <!-- 设置模式:REPORT_ONLY仅上报不拦截,ENFORCING直接拦截 --> <constant name="struts.security.csp.mode" value="REPORT_ONLY" /> <!-- 自定义script-src规则,Struts会自动生成并注入nonce值 --> <constant name="struts.security.csp.policy.script-src" value="'self' 'unsafe-inline' 'nonce-${nonce}' http: https:" /> <!-- 按需配置其他规则 --> <constant name="struts.security.csp.policy.default-src" value="'self'" /> <constant name="struts.security.csp.policy.style-src" value="'self' 'unsafe-inline'" /> </struts>
- 使用Struts自动生成的nonce值
若依赖nonce信任脚本,需在JSP中引用Struts注入的${nonce}变量,而非手动写死:
<script src="${pageContext.request.contextPath}/html/js/jquery/jquery-1.8.3.min.js" type="text/javascript" nonce="${nonce}"></script>
- 清理无效配置
移除所有手动添加的CSP Meta标签,以及自定义拦截器中的CSP响应头,避免规则冲突。
关键说明
strict-dynamic仅信任由已信任脚本动态加载的资源,直接通过标签加载的脚本需显式加入script-src白名单,或用nonce/hash标记信任。- Struts2 6.x内置CSP拦截器优先级高于自定义响应头和Meta标签,必须通过Struts配置修改规则。
内容的提问来源于stack exchange,提问作者Sebastian Ruiz

