本文于2016年4月底完成,發(fā)布在個(gè)人博客網(wǎng)站上。
考慮個(gè)人博客因某種原因無法修復(fù),于是在博客園安家,之前發(fā)布的文章逐步搬遷過來。
詭異的問題
AppScan掃描報(bào)告中提示,Web服務(wù)器返回js
、css
、png
、jsp
頁面的HTTP響應(yīng)中缺少安全頭部。HTTP的安全頭部包括HTTP Strict Transport Security
、X-Frame-Options
、X-Content-Type-Options
、X-XSS-Protection
、Content-Security-Policy
。
網(wǎng)上資料很多,于是參照資料修改$CATALINA_BASE/conf/web.xml
,增加相關(guān)配置,如下是樣例:
<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的配置相關(guān)。 -->
</filter-mapping>
本以為這樣修改之后問題就解決了,所以也沒用瀏覽器的調(diào)試面板去仔細(xì)檢查Web服務(wù)器響應(yīng)數(shù)據(jù)的HTTP頭部;但天不遂人愿,事情并沒有如預(yù)想的方向發(fā)展。
在稍后的一份AppScan掃描報(bào)告中,居然又看到了Web服務(wù)器返回的HTTP響應(yīng)缺少安全頭部的提示。不過這次稍有區(qū)別,報(bào)告中只提示jsp
頁面的訪問存在問題。于是使用瀏覽器的調(diào)試面板仔細(xì)查看Web服務(wù)器返回的響應(yīng)信息,發(fā)現(xiàn)Web服務(wù)器返回js
、css
、png
時(shí),在HTTP響應(yīng)中增加了必要的頭部,如下所示:
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
頁面的響應(yīng)中并沒有相應(yīng)增加安全頭部,如下所示,導(dǎo)致AppScan報(bào)告中Web服務(wù)器返回的HTTP響應(yīng)缺少安全頭部問題依然存在。
Content-Type:text/html;charset=UTF-8
Date:Tue, 24 May 2016 16:18:30 GMT
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
同部門內(nèi)有一個(gè)A項(xiàng)目,這個(gè)項(xiàng)目有10年開發(fā)、維護(hù)的歷史,歷經(jīng)公司安全紅線多輪整改,項(xiàng)目成員積累了相當(dāng)豐富的斗爭經(jīng)驗(yàn),在處理AppScan掃描報(bào)告上也有相當(dāng)?shù)慕?jīng)驗(yàn)。于是就安全頭部的整改方法咨詢A項(xiàng)目的MDE,希望可以獲得關(guān)鍵信息。
A項(xiàng)目的MDE為人很爽快,介紹了他們的經(jīng)驗(yàn),總結(jié)下有如下幾點(diǎn):
- A項(xiàng)目在整改AppScan掃描問題時(shí),確實(shí)遇到過類似的問題,解決的方法是給響應(yīng)增加安全頭部。
- 但A項(xiàng)目使用了自定義的過濾器來給HTTP響應(yīng)增加安全頭部,并沒有使用Apache Tomcat官方提供的過濾器,原因是A項(xiàng)目使用的Tomcat版本太低,出于業(yè)務(wù)原因暫不好升級。
- A項(xiàng)目增加自定義的過濾器之后,“Web服務(wù)器返回的HTTP響應(yīng)缺少安全頭部”就從AppScan掃描報(bào)告中消失了。
但壞消息是A項(xiàng)目團(tuán)隊(duì)沒有遇到過前述的問題,自然沒有處理類似問題的經(jīng)驗(yàn)可供參考。這就詭異了,為什么Web服務(wù)器對jsp
的響應(yīng)沒有增加安全頭部呢?
分析過程
當(dāng)前的項(xiàng)目使用了Spring+Struts2+iBatis,從技術(shù)組合上可以說非常傳統(tǒng),但在技術(shù)應(yīng)用上存在很大不同。為了描述方便,下面把存在問題的項(xiàng)目稱為B項(xiàng)目。
檢查項(xiàng)目配置
重溫項(xiàng)目的配置情況。
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配置文件,內(nèi)容如下:
<?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
的實(shí)現(xiàn)代碼中有玄機(jī)?
找到HttpHeaderSecurityFilter
類的代碼,如下是增加頭部的實(shí)現(xiàn)。
@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);
}
代碼很簡單,沒發(fā)現(xiàn)對jsp的訪問有做過什么特別的處理。
對頁面訪問的影響
依據(jù)前述配置,頁面訪問流程如下所示:
- 瀏覽器請求頁面時(shí),Web服務(wù)端的Struts2攔截頁面訪問請求;
- Web服務(wù)端的通用Action接收請求,并將請求重定向至對應(yīng)的jsp頁面;
- 由于沒有使用Action向頁面?zhèn)鬟f數(shù)據(jù),所以開發(fā)人員需要在頁面上使用ajax方式向Web服務(wù)端請求業(yè)務(wù)數(shù)據(jù);
進(jìn)一步分析
仔細(xì)回想了A項(xiàng)目的特點(diǎn),以及與B項(xiàng)目的差異點(diǎn)。
A項(xiàng)目也使用了Spring+Struts的組合,但和B項(xiàng)目有個(gè)顯著不同點(diǎn),B項(xiàng)目是Struts2的重度使用用戶,項(xiàng)目中的jsp
全部使用action做了包裝,用戶在地址欄看不到jsp
結(jié)尾的URL。
而B項(xiàng)目雖然使用了Spring+Struts的組合,但實(shí)際上僅僅使用了Struts2提供的國際化和s標(biāo)簽,代碼中定義的Action僅用于轉(zhuǎn)發(fā)請求至jsp
,用戶在瀏覽器的地址欄里可以明確的看到當(dāng)前頁面的jsp
文件名和路徑。
如下是A項(xiàng)目struts.xml文件中action后綴的配置
<constant name="struts.action.extension" value="action"/>
如下是B項(xiàng)目struts.xml文件中action后續(xù)的配置
<constant name="struts.action.extension" value="jsp,action"/>
問題在于A項(xiàng)目并沒有遇到B項(xiàng)目現(xiàn)在遇到的問題。
分析到這里,嘗試調(diào)整struts.xml的配置,去掉配置中的jsp
,如下所示
<constant name="struts.action.extension" value="action"/>
這樣action后綴的配置和A項(xiàng)目保持一致。
重啟應(yīng)用之后,使用Google Chrome提供的調(diào)試面板,檢查Web服務(wù)器對jsp
頁面的響應(yīng),發(fā)現(xiàn)居然有HTTP安全頭部。這說明,action后綴的配置對安全頭部的生成有影響,但具體什么影響還未知,并且出于技術(shù)原因,目前并不能調(diào)整action后綴的配置。因此這問題還不算完,需要繼續(xù)分析。
依據(jù)J2EE規(guī)范中Filter和Servlet的定義,我們知道Filter在執(zhí)行時(shí)需要等待Servlet完成處理并寫出響應(yīng)后才會逐個(gè)返回,因此觀察Servlet的運(yùn)行棧,可以看到Web請求的處理路徑。既然調(diào)整action后綴的配置對安全頭部的生成有影響,那么說明不同的配置條件下,jsp的執(zhí)行路徑是有差異的,因此觀察運(yùn)行棧一定可以發(fā)現(xiàn)點(diǎn)什么。
但問題是對于代碼里的Servlet類,可以使用eclipse的調(diào)試手段,在代碼里打上斷點(diǎn),觀察執(zhí)行棧,但對于jsp來說,使用打斷點(diǎn)來檢查棧的方法就行不通了。那怎么辦呢?
其實(shí)方法很簡單,jsp頁面內(nèi)可以寫Java代碼,因此可以在頁面上定義一個(gè)java.lang.Throwable
對象,然后使用該對象來輸出當(dāng)前調(diào)用棧。代碼樣例如下所示:
<%
new Throwable().printStackTrace();
%>
于是調(diào)整action后綴的配置,使用瀏覽器訪問頁面,提取頁面生成的棧。
如下是action后綴配置為jsp,action
時(shí)的棧。
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
時(shí)的棧。
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)
對比之下,有如下發(fā)現(xiàn):
-
當(dāng)action后續(xù)為
jsp,action
時(shí)- 棧信息很長。
- 棧中出現(xiàn)了很多Struts2相關(guān)的棧幀(stack frame),說明頁面訪問請求被Struts2的過濾器攔截,符合預(yù)期。
- 棧中未出現(xiàn)
HttpHeaderSecurityFilter
相關(guān)的棧幀(stack frame)。 -
Log4jServletFilter
相關(guān)的棧幀(stack frame)出現(xiàn)了兩次,為什么?
-
當(dāng)前action后綴為
action
時(shí)- 棧信息很短。
- 棧中沒有Struts2相關(guān)的棧幀(stack frame),說明頁面訪問請求沒有被被Struts2的過濾器攔截,符合預(yù)期。
- 棧中出現(xiàn)了
HttpHeaderSecurityFilter
相關(guān)的棧幀(stack frame)。 -
Log4jServletFilter
相關(guān)的棧幀(stack frame)出現(xiàn)了一次,有點(diǎn)意思。
舊的問題沒解決,新的問題又出現(xiàn)了。action后綴的配置,看來不單對HttpHeaderSecurityFilter
產(chǎn)生了影響,對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>
重啟應(yīng)用之后使用瀏覽器的調(diào)試面板觀察頁面的響應(yīng)數(shù)據(jù),久違的HTTP安全頭部終于出現(xiàn)了。
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
這時(shí),檢查棧信息,可以看到HttpHeaderSecurityFilter
相關(guān)的棧幀(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)
定位結(jié)論
折騰這么久,終于把解決方法整出來了,其實(shí)很簡單。
當(dāng)前struts.xml中有如下配置
<constant name="struts.action.extension" value="action,jsp"/>
配置安全頭部的過濾器時(shí),需要在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>
原因應(yīng)該和Struts2重定向請求至頁面的方式相關(guān),不過暫時(shí)沒有時(shí)間去研究Struts2,期望后續(xù)會有所了解。文章來源:http://www.zghlxwxcb.cn/news/detail-779072.html
資料
關(guān)于dispatcher
的一些資料。文章來源地址http://www.zghlxwxcb.cn/news/detail-779072.html
- web.xml里
中的 作用 - Web.xml中Filter過濾器標(biāo)簽幾個(gè)說明
- filter-mapping的執(zhí)行順序和字符集設(shè)置的優(yōu)先級
- Filtering Requests and Responses
到了這里,關(guān)于HTTP安全頭部對jsp頁面不生效的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!