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>