Default escaping in Freemarker

橙三吉。 提交于 2019-11-30 12:43:25

To elaborate on Attila's answer: you can use a class like this one and then wrap your template loader like this:

final TemplateLoader templateLoader = new ClassTemplateLoader(this.getClass(), templatePath) {
  /**
   * Replaces the normal template reader with something that changes the default
   * escaping to HTML as to avoid XSS attacks.
   */
  @Override
  public Reader getReader(Object templateSource, String encoding) throws IOException {
     return new WrappingReader(super.getReader(templateSource, encoding), "<#escape x as x?html>", "</#escape>");
  }
};

If you don't include linebreaks in the added parts you don't get the line numbering problem. You can't use the <#ftl>/[#ftl] with this approach, though.

Since 2.3.24 each template has an associated freemarker.core.OutputFormat object, which specifies if and how ${...} (and #{...}) is escaped. OuputFormat for HTML, XML and RTF are provided out of the box, but you can also define your own formats. When the selected OutputFormat escapes by default, then you can prevent escaping explicitly like ${foo?no_esc}.

There are several ways of associating templates with the OutputFormat you want. For HTML and XML escaping, the recommended way is setting the recognize_standard_file_extensions configuration setting to true, then using ftlh file extension for HTML, and ftlx file extension for XML templates. You can also associate OutputFormat-s to templates based on arbitrary template name (template path) patterns, using the template_configurers setting. Last not least, you can just set the default output format globally like configuration.setOutputFormat(HTMLOutputFormat.INSTANCE). You can also override the output format at the top of the template as <#ftl output_format='HTML'>, though it should be used rarely.

Related documentation pages: http://freemarker.org/docs/dgui_misc_autoescaping.html, http://freemarker.org/docs/pgui_config_outputformatsautoesc.html

There is a solution, although it's not entirely trivial. You can create a special TemplateLoader that wraps other template loaders, and injects <#escape x as x?html> in the prolog of the template source text, and adds as the epilogue of it.

Obvious drawbacks: - column numbers in first line will be thrown off - if your template starts with <#ftl> declaration, you need to insert <#escape> after it.

The suggested TemplateLoaders in the links need a little tweaking if you are using <#include parse=false .../> to include for example HTML in your templates.

Also, you need to copy spring.ftl and use your own copy with the <#ftl ..> directive on top removed like Tom said.

The following works well, although a bit rough (using guava over commons-io)

@Override  
public Reader getReader(Object pTemplateSource, String pEncoding) throws IOException {  
   Reader tReader = delegate.getReader(pTemplateSource, pEncoding);  
   try {  
       String tTemplateText = CharStreams.toString(tReader);

       //only include files ending with "ftl", as we may have some parse=false on included html files
       if (pTemplateSource.toString().endsWith("ftl")) {
           return new StringReader(ESCAPE_PREFIX + tTemplateText + ESCAPE_SUFFIX);
       }
       return new StringReader(tTemplateText);
   } finally {  
       Closeables.closeQuietly(tReader);  
   }  
} 

You don't actually need a WrappingReader to add the escapes. You can just create a decorator around any TemplateLoader, read in the template into a String, wrap the template text in escapes and then return a StringReader that reads the resulting String. To see how that's done take a look here. The only gotcha that I've found is that if you use this approach and include the spring.ftl macros from the classpath they will blow up since they have a <#ftl> declaration at the very top. You can however simply copy spring.ftl into your template path and remove the declaration (and all the escaping directives since you will be escaping by default).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!