分类目录归档:Spring

Spring 3.0配置多个DispatcherServlet的方法及注意事项


项目中需要同时用到两个视图解析器,一个报表的,一个jsp的。这就产生了标题所述的需求。通过阅读Spring Framework参考手册以及示例,解决了这个问题,中间也走了不少弯路,碰到了不少问题,特记录下来。

配置多个DispatcherServlet有多种方法,一种是在DispatcherServlet中直接指定此DispatcherServlet对应的配置文件。例如:

<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/app-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>

但是这种配置方法存在一个问题:由于DispatcherServlet对应的Controller需要访问Service并且使用dataSource,而DispatcherServlet之间的上下文是分离的,势必会出现多个dataSource的情况,降低了数据库连接使用的效率。

后来找到了第二种方法,目前正在使用的:DispatcherServlet不指定具体的配置文件,并且DispatcherServlet只初始化自己需要使用到的Controller类,然后由org.springframework.web.context.ContextLoaderListener初始化除Controller以外的全部对象。这样子多个dataSource的问题解决了。配置如下: 继续阅读

Spring MVC 如何防止XSS、SQL注入攻击


在Web项目中,通常需要处理XSS,SQL注入攻击,解决这个问题有两个思路:

  • 在数据进入数据库之前对非法字符进行转义,在更新和显示的时候将非法字符还原
  • 在显示的时候对非法字符进行转义

如果项目还处在起步阶段,建议使用第二种,直接使用jstl的<c:out>标签即可解决非法字符的问题。当然,对于Javascript还需要自己处理一下,写一个方法,在解析从服务器端获取的数据时执行以下escapeHTML()即可。

附:Javascript方法:

String.prototype.escapeHTML = function () {
return this.replace(/&/g, ‘&amp;’).replace(/>/g, ‘&gt;’).replace(/</g, ‘&lt;’).replace(/”/g, ‘&quot;’);
}

如果项目已经开发完成了,又不想大批量改动页面的话,可以采用第一种方法,此时需要借助Spring MVC的@InitBinder以及org.apache.commons.lang.PropertyEditorSupport、org.apache.commons.lang.StringEscapeUtils

public class StringEscapeEditor extends PropertyEditorSupport {
private boolean escapeHTML;
private boolean escapeJavaScript;
private boolean escapeSQL;

public StringEscapeEditor() { super(); }
public StringEscapeEditor(boolean escapeHTML, boolean escapeJavaScript, boolean escapeSQL) {
super();
this.escapeHTML = escapeHTML;
this.escapeJavaScript = escapeJavaScript;
this.escapeSQL = escapeSQL;
}

@Override
public void setAsText(String text) {
if (text == null) {
setValue(null);
} else {
String value = text;
if (escapeHTML) {   value = StringEscapeUtils.escapeHtml(value);     }
if (escapeJavaScript) {     value = StringEscapeUtils.escapeJavaScript(value);     }
if (escapeSQL) {     value = StringEscapeUtils.escapeSql(value);     }     setValue(value);     }
}

@Override
public String getAsText() {     Object value = getValue();     return value != null ? value.toString() : “”;    }
}

在使用StringEscapeUtils时需要注意escapeHtml和escapeJavascript方法会把中文字符转换成Unicode编码,如果通过<c:out>标签或者EL表达式展示时,能够正确还原,但是如果使用类似于Ext这样的前端组件来展示这部分内容时,不能正常还原,这也是我为什么放弃了第一种方法,直接使用第二种方法的原因。

在上面我们做了一个EscapeEditor,下面还要将这个Editor和Spring的Controller绑定,使服务器端接收到数据之后能够自动转移特殊字符。
下面我们在@Controller中注册@InitBinder

@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringEscapeEditor(false, false, false));
}

这个方法可以直接放到abstract Controller类中,这样子每个Controller实例都能够拥有该方法。至此第二种方法完成,但是在还原的方法暂时还没有。O(∩_∩)O…

Spring MVC JasperReport 导出Html格式报表图片不显示


最近这个项目使用了JasperReport组件,最开始使用的时候,是配置成了由org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView对报表模板进行解析,这样的配置可以带来一些好处,比如可以根据format关键字不同,动态生成html,pdf,xls,csv四种类型的报表,但是最近在使用的时候碰到问题了。

设计了一种图表结合的报表,使用html,pdf导出的时候图片能够正常显示 ,但是导出html格式后,报表中的图不见了。

在网上查找后发现需要设置下面四个参数:

  • JRHtmlExporterParameter.IMAGES_DIR_NAME
  • JRHtmlExporterParameter.IMAGES_URI
  • JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN
  • JRHtmlExporterParameter.IS_OUTPUT_IMAGES_TO_DIR

一开始是想结合Spring MVC的配置设置的,但是使用exporterParameters无法将上述参数设置到JasperReportHtmlView中,只好作罢。动手自己写了一个。步骤如下:

  1. 获得报表模板存放路径。
    String jasperFilePath = new StringBuilder(“WEB-INF/reports/”).append(jasperFileName).append(“.jasper”).toString();
  2. 获得报表模板文件
    File reportFile = getWebApplicationContext(request).getResource(jasperFilePath).getFile();
  3. 根据报表模板文件生成报表打印对象
    JasperPrint print = JasperFillManager.fillReport(reportFile.getAbsolutePath(), model, dataSource.getConnection());
  4. 创建Html导出对象
    JRHtmlExporter exporter = new JRHtmlExporter();
  5. 设置图片文件存放路径,此路径为服务器上的绝对路径
    String imageDIR = webContext.getResource(“reportFiles”).getFile().getAbsolutePath();
    exporter.setParameter(JRHtmlExporterParameter.IMAGES_DIR_NAME, imageDIR);
  6. 设置图片请求URI
    String imageURI = request.getContextPath() + “/reportFiles/”;
    exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, imageURI);
  7. 设置导出图片到图片存放路径
    exporter.setParameter(JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN, Boolean.TRUE);
    exporter.setParameter(JRHtmlExporterParameter.IS_OUTPUT_IMAGES_TO_DIR, Boolean.TRUE);
  8. 设置导出对象
    exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
  9. 设置导出方法
    exporter.setParameter(JRExporterParameter.OUTPUT_WRITER, response.getWriter());
  10. 防止图片第一次不显示
    request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE, print);
  11. 设置HTTP Head
    response.setContentType(“text/html”);
  12. 执行导出
    exporter.exportReport();

至此,自定义的导出方法完成了。 需要特别注意的是JRHtmlExporterParameter.IMAGES_DIR_NAME和JRHtmlExporterParameter.IMAGES_URI虽然执行的是相同的目录,但是表达方式不一样,IMAGES_DIR_NAME是文件系统的绝对路径,而IMAGES_URI是http请求时的uri。

Spring+BlazeDS+Flex+SpringSecurity配置


最近一个项目用到了Spring +BlazeDS+Flex+SpringSecurity的配置,在这里记录一下此种框架的配置方法,仅作备忘。

web.xml

<!– 初始化spring关联配置,比如SpringSecurity, Hibernate等等 –>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!– 日志服务监听器 –>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!– flexhttp请求监听器 –>
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<!– SpringSecurity HTTP访问过滤器,实现HTTP请求拦截 –>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!– 编码过滤器,经过测试,flex在进行数据传输的时候,不需要此过滤器也能正确传输中文,此处主要是为了非flex类型的请求配置 –>
<filter>
<filter-name>encoding-filter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!– SpringMVC REST服务需要,因为浏览器的HTTP不支持PUT,DELETE METHOD,通过这个过滤器可以使用POST伪装PUT,DELETE METHOD实现 –>
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!– Hibenate 过滤器,保证一个HTTP请求使用同一个Session,并且提供了flushMode,使程序员在开发的时候不必关心缓存刷新的问题 –>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param> 继续阅读

如何配置Spring Security的global-method-security属性


最近的这个项目是Flex + Spring + Hibernate + Spring Security的技术框架。

由于Flex需要和SpringIOC管理的Service对象进行数据交换,所以用到了Flex称为AMF的技术,实际上就是远程调用。

通常来说,Web类型的项目使用Spring Security做到基于URL的安全控制也就行了。但是现在这个项目因为AMF的缘故,暴露了Service对象,对于这部分内容也需要使用Spring Security做统一的访问控制。

技术上,SpringSecurity能够提供了方法级别的安全控制,不过在配置的时候还是碰到了不少的问题,在此记录下来,供自己以后查阅,也希望能够帮到需要帮助的同仁。

Spring Security提供了两种方法实现方法级安全控制。
第一种,在方法上添加注解,这种方法除了能够控制此方法允许访问的角色, 还能够与ACL一起提供基于数据级的访问控制。但是此种方法会令权限控制代码分散在Java代码中,为今后的维护和变更带来了风险。
第二种,利用aspectj框架,在配置文件中编写方法访问控制规则,基于面向切面编程的思想,访问控制代码完全和业务代码分离,开发人员无需关心权限控制的问题,但是需要在开发之前定义好业务代码方法命名规则。此种方法虽然能够比较完美的把权限控制代码和业务代码分离,但是不能结合ACL做数据级权限控制。

因为目前的项目业务简单,并且还未使用ACL机制,所以准备以aspectj为主,辅以注解的形式控制方法安全。

首先<http>部分的配置和普通的Web项目配置没有什么两样,不再做介绍,最重要的是<global-method-security>的位置。下面是我配置以及发现问题的过程:

按照官方的sample首先解决了aspectj的依赖问题,测试发现<http>部分基于URL过滤的控制已经生效,但是在远程调用这块的控制始终不能成功,并且项目在启动的时候也没有任何异常信息。 继续阅读

如何正确释放Hibernate Session使用的资源


最近一个项目使用的SpringMVC+SpringSecurity+Hibernate的开发框架,由于需要使用到多个视图表现方式,所以在Web.xml中针对不同的视图表现方式配置了不同的初始化入口,这直接导致一个项目中存在多个连接缓冲池,其中SpringSecurity单独使用一个连接缓冲池,业务代码单独使用一个,这就引出来下面的问题。

在业务代码中,由于在Service层进行了事务控制,所以业务代码的DAO继承HibernateDaoSupport之后,直接使用getSession().createSQLQuery的方式操作数据库,不用自己管理session。

而在SpirngSecurity中,由于自己实现了UserDetailsService接口,在自定义的UserDetailsService中查询了数据库,获得用户信息。在做压力测试的时候发现很容易就出现can’t open connection的问题,并且在停止压力测试之后,已经打开的链接并不不会被关闭。通过检查代码发现,在UserDetailsService中并没有进行事务控制,也没有对Session进行释放资源的操作,问题就出在这里了。

由于业务代码使用了事务控制,在事务结束的时候框架会释放Session关联的资源,而UserDetailsService中没有事务控制,所以我们需要手动进行Session相关资源的释放。将getSession().createSQLQuery修改为Session sess = getSession(); sess.createSQLQuery,查询完数据之后执行super.releaseSession(sess);释放Session资源。这样子操作之后,没有再出现无法打开连接的问题,并且在Connection空闲指定时间之后,自动关闭了。

进一步阅读super.releaseSession()方法的代码发现,在HibernateDaoSupport中是通过SeesionFacotryUtils.releaseSession(Session, SessionFactory)进行Session资源释放的,在这个方法中,对Session进行了是否存在事务的判断,如果Session本身仍然在事务内,则不关闭。想必,如果业务层存在事务控制,同时在DAO进行releaseSession的操作时,Session并不会真正的被关闭,而要等到事务结束的时候由事务进行关闭操作了。

Spring 3 使用Ant编译问题(No parameter name specified…)


最近一个项目用到了Spring MVC,在代码中使用了@RequestParam和@PathVariable两个注解。最近碰到了一个奇怪的问题,使用了这两个注解的程序,在eclipse+tomcat的环境中运行正常,但是拿到ant+tomcat的环境中就不正常了。

提示下面的错误:

No parameter name specified for argument of type [java.lang.String], and no parameter name information found in class file either.

查看页面提交的参数个数已经名称,甚至顺序,都没有问题。怀疑是ant编译的问题。

最后在http://stackoverflow.com/questions/2622018/compile-classfile-issue-in-spring-3找到了答案。原来eclipse默认编译的代码属于Debug级别,而@RequestParam和@PathVariable两个注解的名称绑定只在Debug级别才会使用默认值(也就是后面的参数名称),而ant编译的时候,debug级别默认是关闭的,通过打开javac debug=”on”可以解决这个问题。

不过这也是治标不治本。最终还是通过修改代码:在每个使用@RequestParam和@PathVariable两个注解的地方都明确指定参数名,再使用ant+tomcat环境运行代码,没有再出现问题。