freemarker作为"通用"模版引擎, 默认情况下不会对model中的值进行html转义, 然而在web项目中, 为了防止跨站脚本攻击等问题, 必须在对model中的值进行转义.
解决办法:
方法1. 是使用 ${x?html} 可以用于对单个值的转义
方法2. 使用<#escape x as x?html> ... </#escape> 将需要转义的html代码包起来, 这样其中所有的值都会被转义了.
毫无疑问这两个方法都需要大量的重复操作, 如果我所有的模板都需要转义, 有没有一劳永逸的办法呢?
方法3. 使用自定义TemplateLoader
首先我们需要实现一个TemplateLoader. 代码如下:
public class HtmlTemplateLoader implements TemplateLoader { private static final String HTML_ESCAPE_PREFIX= "<#escape x as x?html>"; private static final String HTML_ESCAPE_SUFFIX = "</#escape>"; private final TemplateLoader delegate; public HtmlTemplateLoader(TemplateLoader delegate) { this.delegate = delegate; } @Override public Object findTemplateSource(String name) throws IOException { return delegate.findTemplateSource(name); } @Override public long getLastModified(Object templateSource) { return delegate.getLastModified(templateSource); } @Override public Reader getReader(Object templateSource, String encoding) throws IOException { Reader reader = delegate.getReader(templateSource, encoding); String templateText = IOUtils.toString(reader); return new StringReader(HTML_ESCAPE_PREFIX+templateText + HTML_ESCAPE_SUFFIX); } @Override public void closeTemplateSource(Object templateSource) throws IOException { delegate.closeTemplateSource(templateSource); } }
这里使用了一个delegate模式, 因为我们只需要对getReader()方法做小幅的更改, (其他的则委托给默认的TemplateLoader去做就可以), 实现方式很简单, 就是在读取template文件之后, 在前后套上<#escape>标签而已.
为了和SpringMVC结合起来使用呢, 我们还需要自定义一个FreeMarkerConfigurer.
public class CustomFreeMarkerConfigurer extends FreeMarkerConfigurer{ @Override protected TemplateLoader getAggregateTemplateLoader(List<TemplateLoader> templateLoaders) {
return new HtmlTemplateLoader(super.getAggregateTemplateLoader(templateLoaders));
} }
然后在spring的xml配置中, 使用它来代替默认的FreeMarkerConfigurer即可.
<bean id="freemarkerConfigurer" class="com.ananda.oa.web.assistant.freemarker.AnandaFreeMarkerConfigurer"> <!-- 其他配置跟之前相同 --> </bean>