为了防止CSRF攻击,在struts2的form表单中使用<s:token/>,在拦截器里添加了<interceptor-ref name=”token”/>,结果发现抛出下面的异常:
java.lang.NullPointerException
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:630)
at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:606)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:210)
at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:139)
at org.apache.struts2.interceptor.TokenInterceptor.getErrorMessage(TokenInterceptor.java:182)
at org.apache.struts2.interceptor.TokenInterceptor.handleInvalidToken(TokenInterceptor.java:166)
at org.apache.struts2.interceptor.TokenInterceptor.handleToken(TokenInterceptor.java:151)
at org.apache.struts2.interceptor.TokenInterceptor.doIntercept(TokenInterceptor.java:142)
at com.alibaba.search.scc.web.interceptor.SccTokenInterceptor.intercept(SccTokenInterceptor.java:42)
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:562)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
我的用法照理讲应该是官方的用法,按照异常堆栈查看struts2源码,发现问题在这里:
Java
@Inject public void setTextProvider(TextProvider textProvider) { this.textProvider = textProvider; } protected String handleInvalidToken(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); String errorMessage = getErrorMessage(invocation); if (action instanceof ValidationAware) { ((ValidationAware) action).addActionError(errorMessage); } else { log.warn(errorMessage); } return INVALID_TOKEN_CODE; } protected String getErrorMessage(ActionInvocation invocation) { Object action = invocation.getAction(); if (action instanceof TextProvider) { return ((TextProvider) action).getText(INVALID_TOKEN_MESSAGE_KEY, DEFAULT_ERROR_MESSAGE); } return textProvider.getText(INVALID_TOKEN_MESSAGE_KEY, DEFAULT_ERROR_MESSAGE); }
@Inject public void setTextProvider ( TextProvider textProvider ) { this . textProvider = textProvider ; } protected String handleInvalidToken ( ActionInvocation invocation ) throws Exception { Object action = invocation . getAction ( ) ; String errorMessage = getErrorMessage ( invocation ) ; if ( action instanceof ValidationAware ) { ( ( ValidationAware ) action ) . addActionError ( errorMessage ) ; } else { log . warn ( errorMessage ) ; } return INVALID_TOKEN_CODE ; } protected String getErrorMessage ( ActionInvocation invocation ) { Object action = invocation . getAction ( ) ; if ( action instanceof TextProvider ) { return ( ( TextProvider ) action ) . getText ( INVALID_TOKEN_MESSAGE_KEY , DEFAULT_ERROR_MESSAGE ) ; } return textProvider . getText ( INVALID_TOKEN_MESSAGE_KEY , DEFAULT_ERROR_MESSAGE ) ; } |
出问题的是textProvider.getText方法,原因是textProvider中的bundle和class是空的,为什么是空的呢?可以看到setTextProvider方法上有一个@Inject注解,这个注解告诉了struts2需要注入一个类型为 com.opensymphony.xwork2.TextProvider 的实例,因为struts jar包里面的 struts-default.xml 中默认的配置是:
Java
<span class="code-tag" style="color: #000091;"><bean type=<span class="code-quote" style="color: #009100;">"com.opensymphony.xwork2.TextProvider"</span> name=<span class="code-quote" style="color: #009100;">"struts"</span> class=<span class="code-quote" style="color: #009100;">"com.opensymphony.xwork2.TextProviderSupport"</span> /></span>
< span class = "code-tag" style = "color: #000091;" > & lt ; bean type = < span class = "code-quote" style = "color: #009100;" > "com.opensymphony.xwork2.TextProvider" < / span > name = < span class = "code-quote" style = "color: #009100;" > "struts" < / span > class = < span class = "code-quote" style = "color: #009100;" > "com.opensymphony.xwork2.TextProviderSupport" < / span > / & gt ; < / span > |
所以默认注入的就是TextProviderSupport实例对象,而这个bean中没有默认指定class和bundle,这就导致了textProvider.getText方法抛出空指针异常。
根据官方文档中的说法,可以自己实现TextProvider,形如:
<bean class=”org.demo.MyTextProvider” name=”myTextProvider” type=”com.opensymphony.xwork2.TextProvider” />
<constant name=”struts.xworkTextProvider” value=”myTextProvider” />
换个方式解决问题:
如果上面的getErrorMessage方法中执行了if语句里面的内容就不会走到textProvider.getText方法那里去,所以可以尝试让action的类型属于TextProvider,在TextProvider上ctrl+T可以看到其实现类上有ActionSupport类,所以这就知道了只需要让action类继承ActionSupport类即可避免抛出上面的空指针异常。
当然,既然进入到handleInvalidToken方法里面来了,那就需要为返回值INVALID_TOKEN_CODE(“invalid.token“)配置出错页面,形如:
XHTML
<global-results> <result name="invalid.token" type="dispatcher"> /templates/error/illegal_operate.jsp </result> </global-results>
<global-results> <result name = "invalid.token" type = "dispatcher" > /templates/error/illegal_operate.jsp </result> </global-results> |
联系客服