HTTP安全头部对jsp页面不生效
本文于2016年4月底完成,發布在個人博客網站上。
考慮個人博客因某種原因無法修復,于是在博客園安家,之前發布的文章逐步搬遷過來。
詭異的問題
AppScan掃描報告中提示,Web服務器返回js、css、png、jsp頁面的HTTP響應中缺少安全頭部。HTTP的安全頭部包括HTTP Strict Transport Security、X-Frame-Options、X-Content-Type-Options、X-XSS-Protection、Content-Security-Policy。
網上資料很多,于是參照資料修改$CATALINA_BASE/conf/web.xml,增加相關配置,如下是樣例:
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern> <!-- 注意:Jackie遇到的問題與httpHeaderSecurity的配置相關。 -->
</filter-mapping>
本以為這樣修改之后問題就解決了,所以也沒用瀏覽器的調試面板去仔細檢查Web服務器響應數據的HTTP頭部;但天不遂人愿,事情并沒有如預想的方向發展。
在稍后的一份AppScan掃描報告中,居然又看到了Web服務器返回的HTTP響應缺少安全頭部的提示。不過這次稍有區別,報告中只提示jsp頁面的訪問存在問題。于是使用瀏覽器的調試面板仔細查看Web服務器返回的響應信息,發現Web服務器返回js、css、png時,在HTTP響應中增加了必要的頭部,如下所示:
Cache-Control:private
Content-Type:image/png
Date:Sun, 10 Apr 2016 13:16:26 GMT
Expires:Thu, 01 Jan 1970 08:00:00 CST
Server:Apache-Coyote/1.1
Strict-Transport-Security:max-age=0
Transfer-Encoding:chunked
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
這說明安全頭部的配置生效了,但詭異的是jsp頁面的響應中并沒有相應增加安全頭部,如下所示,導致AppScan報告中Web服務器返回的HTTP響應缺少安全頭部問題依然存在。
Content-Type:text/html;charset=UTF-8
Date:Tue, 24 May 2016 16:18:30 GMT
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
同部門內有一個A項目,這個項目有10年開發、維護的歷史,歷經公司安全紅線多輪整改,項目成員積累了相當豐富的斗爭經驗,在處理AppScan掃描報告上也有相當的經驗。于是就安全頭部的整改方法咨詢A項目的MDE,希望可以獲得關鍵信息。
A項目的MDE為人很爽快,介紹了他們的經驗,總結下有如下幾點:
- A項目在整改AppScan掃描問題時,確實遇到過類似的問題,解決的方法是給響應增加安全頭部。
- 但A項目使用了自定義的過濾器來給HTTP響應增加安全頭部,并沒有使用Apache Tomcat官方提供的過濾器,原因是A項目使用的Tomcat版本太低,出于業務原因暫不好升級。
- A項目增加自定義的過濾器之后,“Web服務器返回的HTTP響應缺少安全頭部”就從AppScan掃描報告中消失了。
但壞消息是A項目團隊沒有遇到過前述的問題,自然沒有處理類似問題的經驗可供參考。這就詭異了,為什么Web服務器對jsp的響應沒有增加安全頭部呢?
分析過程
當前的項目使用了Spring+Struts2+iBatis,從技術組合上可以說非常傳統,但在技術應用上存在很大不同。為了描述方便,下面把存在問題的項目稱為B項目。
檢查項目配置
重溫項目的配置情況。
Struts2的配置
Struts2在web.xml中的配置如下:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
簡化后的struts.xml配置文件,內容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="jsp,action"/> <!-- 注意這里 -->
<constant name="struts.ui.theme" value="java"></constant>
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.i18n.encoding" value="UTF-8" />
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="basicStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack" />
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error"/>
</global-exception-mappings>
<action name="*" class="MainAction">
<result name="success">{1}.jsp</result>
</action>
</package>
</struts>
通用Action類,簡化后的MainAction代碼如下
import com.opensymphony.xwork2.ActionSupport;
public class MainAction extends ActionSupport {
private static final long serialVersionUID = 928135783255954591L;
@Override
public String execute() throws Exception {
return ActionSupport.SUCCESS;
}
}
粗看下來,似乎沒有什么不妥的地方。
安全頭部的配置
依照文檔,重新檢查$CATALINA_BASE/conf/web.xml文件中的配置,如下:
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
沒看出來什么特別的地方,而官方文檔對HttpHeaderSecurityFilter的使用也沒有特別的說明,那是不是HttpHeaderSecurityFilter的實現代碼中有玄機?
找到HttpHeaderSecurityFilter類的代碼,如下是增加頭部的實現。
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (response.isCommitted()) {
throw new ServletException(sm.getString("httpHeaderSecurityFilter.committed"));
}
// HSTS
if (hstsEnabled && request.isSecure()) {
httpResponse.setHeader(HSTS_HEADER_NAME, hstsHeaderValue);
}
// anti click-jacking
if (antiClickJackingEnabled) {
httpResponse.setHeader(ANTI_CLICK_JACKING_HEADER_NAME, antiClickJackingHeaderValue);
}
// Block content type sniffing
if (blockContentTypeSniffingEnabled) {
httpResponse.setHeader(BLOCK_CONTENT_TYPE_SNIFFING_HEADER_NAME,
BLOCK_CONTENT_TYPE_SNIFFING_HEADER_VALUE);
}
// cross-site scripting filter protection
if (xssProtectionEnabled) {
httpResponse.setHeader(XSS_PROTECTION_HEADER_NAME, XSS_PROTECTION_HEADER_VALUE);
}
}
chain.doFilter(request, response);
}
代碼很簡單,沒發現對jsp的訪問有做過什么特別的處理。
對頁面訪問的影響
依據前述配置,頁面訪問流程如下所示:
- 瀏覽器請求頁面時,Web服務端的Struts2攔截頁面訪問請求;
- Web服務端的通用Action接收請求,并將請求重定向至對應的jsp頁面;
- 由于沒有使用Action向頁面傳遞數據,所以開發人員需要在頁面上使用ajax方式向Web服務端請求業務數據;
進一步分析
仔細回想了A項目的特點,以及與B項目的差異點。
A項目也使用了Spring+Struts的組合,但和B項目有個顯著不同點,B項目是Struts2的重度使用用戶,項目中的jsp全部使用action做了包裝,用戶在地址欄看不到jsp結尾的URL。
而B項目雖然使用了Spring+Struts的組合,但實際上僅僅使用了Struts2提供的國際化和s標簽,代碼中定義的Action僅用于轉發請求至jsp,用戶在瀏覽器的地址欄里可以明確的看到當前頁面的jsp文件名和路徑。
如下是A項目struts.xml文件中action后綴的配置
<constant name="struts.action.extension" value="action"/>
如下是B項目struts.xml文件中action后續的配置
<constant name="struts.action.extension" value="jsp,action"/>
問題在于A項目并沒有遇到B項目現在遇到的問題。
分析到這里,嘗試調整struts.xml的配置,去掉配置中的jsp,如下所示
<constant name="struts.action.extension" value="action"/>
這樣action后綴的配置和A項目保持一致。
重啟應用之后,使用Google Chrome提供的調試面板,檢查Web服務器對jsp頁面的響應,發現居然有HTTP安全頭部。這說明,action后綴的配置對安全頭部的生成有影響,但具體什么影響還未知,并且出于技術原因,目前并不能調整action后綴的配置。因此這問題還不算完,需要繼續分析。
依據J2EE規范中Filter和Servlet的定義,我們知道Filter在執行時需要等待Servlet完成處理并寫出響應后才會逐個返回,因此觀察Servlet的運行棧,可以看到Web請求的處理路徑。既然調整action后綴的配置對安全頭部的生成有影響,那么說明不同的配置條件下,jsp的執行路徑是有差異的,因此觀察運行棧一定可以發現點什么。
但問題是對于代碼里的Servlet類,可以使用eclipse的調試手段,在代碼里打上斷點,觀察執行棧,但對于jsp來說,使用打斷點來檢查棧的方法就行不通了。那怎么辦呢?
其實方法很簡單,jsp頁面內可以寫Java代碼,因此可以在頁面上定義一個java.lang.Throwable對象,然后使用該對象來輸出當前調用棧。代碼樣例如下所示:
<%
new Throwable().printStackTrace();
%>
于是調整action后綴的配置,使用瀏覽器訪問頁面,提取頁面生成的棧。
如下是action后綴配置為jsp,action時的棧。
java.lang.Throwable
at org.apache.jsp.index_jsp._jspService(index_jsp.java:115)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:232)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:64)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:702)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:450)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:375)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at org.apache.struts2.dispatcher.ServletDispatcherResult.doExecute(ServletDispatcherResult.java:164)
at org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:191)
at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:372)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:276)
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:567)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:105)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1078)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:757)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1520)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
如下是action后綴配置為action時的棧。
java.lang.Throwable
at org.apache.jsp.index_jsp._jspService(index_jsp.java:115)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:232)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.filters.HttpHeaderSecurityFilter.doFilter(HttpHeaderSecurityFilter.java:120)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:96)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:105)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1078)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:757)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1520)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
對比之下,有如下發現:
-
當action后續為
jsp,action時- 棧信息很長。
- 棧中出現了很多Struts2相關的棧幀(stack frame),說明頁面訪問請求被Struts2的過濾器攔截,符合預期。
- 棧中未出現
HttpHeaderSecurityFilter相關的棧幀(stack frame)。 -
Log4jServletFilter相關的棧幀(stack frame)出現了兩次,為什么?
-
當前action后綴為
action時- 棧信息很短。
- 棧中沒有Struts2相關的棧幀(stack frame),說明頁面訪問請求沒有被被Struts2的過濾器攔截,符合預期。
- 棧中出現了
HttpHeaderSecurityFilter相關的棧幀(stack frame)。 -
Log4jServletFilter相關的棧幀(stack frame)出現了一次,有點意思。
舊的問題沒解決,新的問題又出現了。action后綴的配置,看來不單對HttpHeaderSecurityFilter產生了影響,對Log4jServletFilter的行為也有影響。
于是檢查Log4jServletFilter的配置,如下
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- log4j2-begin -->
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
咦,怎么filter-mapping還可以配置dispatcher,這是什么鬼?先不管它,參照Log4jServletFilter的配置,修改HttpHeaderSecurityFilter的配置信息。
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
重啟應用之后使用瀏覽器的調試面板觀察頁面的響應數據,久違的HTTP安全頭部終于出現了。
Content-Type:text/html;charset=UTF-8
Date:Tue, 24 May 2016 16:15:21 GMT
Server:Apache-Coyote/1.1
Strict-Transport-Security:max-age=0
Transfer-Encoding:chunked
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
這時,檢查棧信息,可以看到HttpHeaderSecurityFilter相關的棧幀(stack frame)。
java.lang.Throwable
at org.apache.jsp.index_jsp._jspService(index_jsp.java:115)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:232)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.filters.HttpHeaderSecurityFilter.doFilter(HttpHeaderSecurityFilter.java:120)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:64)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:702)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:450)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:375)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at org.apache.struts2.dispatcher.ServletDispatcherResult.doExecute(ServletDispatcherResult.java:164)
at org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:191)
at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:372)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:276)
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245)
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:567)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:105)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1078)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:757)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1520)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
定位結論
折騰這么久,終于把解決方法整出來了,其實很簡單。
當前struts.xml中有如下配置
<constant name="struts.action.extension" value="action,jsp"/>
配置安全頭部的過濾器時,需要在URL匹配模式上增加REQUEST和FORWARD。
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
原因應該和Struts2重定向請求至頁面的方式相關,不過暫時沒有時間去研究Struts2,期望后續會有所了解。
資料
關于dispatcher的一些資料。
- web.xml里
中的 作用 - Web.xml中Filter過濾器標簽幾個說明
- filter-mapping的執行順序和字符集設置的優先級
- Filtering Requests and Responses
總結
以上是生活随笔為你收集整理的HTTP安全头部对jsp页面不生效的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vuepress借助jenkins和sv
- 下一篇: 系统架构设计师-第1章计算机组成与体系结