diff --git a/java-realworld/sample/pom.xml b/java-realworld/sample/pom.xml
index 206eaca..ca39d21 100644
--- a/java-realworld/sample/pom.xml
+++ b/java-realworld/sample/pom.xml
@@ -51,6 +51,10 @@
mybatis-spring-boot-starter
3.0.3
+
+ org.springframework.boot
+ spring-boot-starter-freemarker
+
org.springframework.boot
diff --git a/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/FreeMakerDemo.java b/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/FreeMakerDemo.java
new file mode 100644
index 0000000..f10a264
--- /dev/null
+++ b/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/FreeMakerDemo.java
@@ -0,0 +1,79 @@
+package com.example.demo.controller.freemakerdemo;
+
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+@Controller
+@RequestMapping("/freemarker")
+public class FreeMakerDemo {
+ @Autowired
+ private Configuration freemarkerConfig;
+
+ @GetMapping("/template")
+ public void template(String name, Model model, HttpServletResponse response) throws Exception {
+ Template tpl = freemarkerConfig.getTemplate("no-return-template.ftl");
+ model.addAttribute("name", name);
+ String templateText = FreeMarkerTemplateUtils.processTemplateIntoString(tpl, model);
+ response.setContentType("text/html;charset=utf-8");
+ PrintWriter writer = response.getWriter();
+ writer.write(templateText);
+ writer.flush();
+ writer.close();
+ }
+
+ @GetMapping("/welcome")
+ public String welcome(@RequestParam String name, Model model) {
+ if (name == null || name.isEmpty()) {
+ model.addAttribute("name", "Welcome to Safe FreeMarker Demo, try /freemarker/safe/welcome?name=Hacker<>`");
+ } else {
+ model.addAttribute("name", name);
+ }
+ return "welcome";
+ }
+
+ @GetMapping("/welcome-safe")
+ public String safeWelcome(@RequestParam String name, Model model) {
+ if (name == null || name.isEmpty()) {
+ model.addAttribute("name", "Welcome to Safe FreeMarker Demo, try /freemarker/safe/welcome-safe?name=Hacker<>`");
+ return "welcome";
+ } else {
+ model.addAttribute("name", name);
+ }
+ return "welcome-safe";
+ }
+
+ @GetMapping("/welcome-no-model")
+ public String welcomeNoModel() {
+ return "welcome";
+ }
+
+ @GetMapping("/welcome-no-ftl")
+ public String welcomeNoFTL() {
+ return "welcome-no-existed-totally";
+ }
+
+ @ModelAttribute("defaultName")
+ public String getDefaultName() {
+ return "default name for FreeMarker Demo";
+ }
+
+ @GetMapping("/welcome-default-model")
+ public String welcomeDefaultModel() {
+ return "welcome2";
+ }
+
+
+}
diff --git a/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/README.md b/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/README.md
new file mode 100644
index 0000000..c100275
--- /dev/null
+++ b/java-realworld/sample/src/main/java/com/example/demo/controller/freemakerdemo/README.md
@@ -0,0 +1,9 @@
+# Java Freemaker
+
+使用 Springboot Freemaker Starter 启动后,在 Controller Method 中,返回一个 String 则为 freemaker template 的模版名称(文件名)。
+
+找到文件名比较关键。
+
+一般作为一个 Controller 来说,他的方法是一个纯 Literal String,例如为 $ret,则需要在数据库中搜索 f`${ret}\.\w+` 类似的文件存在,一般来说这个文件中的 ${...} 是可以供 SSTI 的点。
+
+一般的模版注入的审计都类似这种情况,Java 中出现裸 TPL 的渲染和调用的机会不多,框架会包裹一系列的用法。
\ No newline at end of file
diff --git a/java-realworld/sample/src/main/resources/application.properties b/java-realworld/sample/src/main/resources/application.properties
index 77b558c..e6ebd72 100644
--- a/java-realworld/sample/src/main/resources/application.properties
+++ b/java-realworld/sample/src/main/resources/application.properties
@@ -5,9 +5,14 @@ spring.datasource.url=jdbc:sqlite:database.db
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.jpa.database-platform=org.hibernate.dialect.SQLiteDialect
+# sqlmap mapper
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.demo.model
+# freemaker
+spring.freemarker.template-loader-path=classpath:/templates/
+spring.freemarker.suffix=.ftl
+
# auto update
spring.jpa.hibernate.ddl-auto=update
diff --git a/java-realworld/sample/src/main/resources/templates/no-return-template.ftl b/java-realworld/sample/src/main/resources/templates/no-return-template.ftl
new file mode 100644
index 0000000..c9e38b4
--- /dev/null
+++ b/java-realworld/sample/src/main/resources/templates/no-return-template.ftl
@@ -0,0 +1,9 @@
+
+
+
+ Welcome
+
+
+Welcome ${name}! If you see this, this is loaded by freemarkerConfig, not Controller Method Return
+
+
\ No newline at end of file
diff --git a/java-realworld/sample/src/main/resources/templates/welcome-safe.ftl b/java-realworld/sample/src/main/resources/templates/welcome-safe.ftl
new file mode 100644
index 0000000..b589c47
--- /dev/null
+++ b/java-realworld/sample/src/main/resources/templates/welcome-safe.ftl
@@ -0,0 +1,9 @@
+
+
+
+ Welcome
+
+
+Welcome ${name?html}!
+
+
\ No newline at end of file
diff --git a/java-realworld/sample/src/main/resources/templates/welcome.ftl b/java-realworld/sample/src/main/resources/templates/welcome.ftl
new file mode 100644
index 0000000..f18907a
--- /dev/null
+++ b/java-realworld/sample/src/main/resources/templates/welcome.ftl
@@ -0,0 +1,9 @@
+
+
+
+ Welcome
+
+
+Welcome ${name}!
+
+
\ No newline at end of file
diff --git a/java-realworld/sample/src/main/resources/templates/welcome2.ftl b/java-realworld/sample/src/main/resources/templates/welcome2.ftl
new file mode 100644
index 0000000..5522eb7
--- /dev/null
+++ b/java-realworld/sample/src/main/resources/templates/welcome2.ftl
@@ -0,0 +1,9 @@
+
+
+
+ Welcome
+
+
+Welcome ${defaultName}!
+
+
\ No newline at end of file