【Spring Boot從入門到進階教程系列 -- SpringMVC配置(包含自定義FastJSON配置)】
下面我們直接開啓代碼之旅
步驟1. 我們可先配置application.properties的Freemarker基本配置,可參考第一篇教程【Spring Boot從入門到進階教程系列 -- 外部Tomcat多方式啓動,加密解密配置數據】
核心配置
########################################################
### freemarker
########################################################
spring.freemarker.allow-request-override=false
spring.freemarker.cache=true
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=/WEB-INF/ftl/
步驟2. 編寫我們的判定角色權限標籤實現類,我們在Freemarker模版頁面使用
預期效果,如果用戶擁有user1或者user2角色,則顯示對應的html內容
<@hasRole role="USER1,USER2">
<a href="#">我擁有USER1或者USER2角色權限</a>
</@hasRole>
@Component
public class HasRoleTag implements TemplateDirectiveModel {
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody directiveBody)
throws TemplateException, IOException {
Object role = params.get("role");
if (StringUtils.isEmpty(role)) {
throw new TemplateException("參數[role]不能爲空", null);
}
if (hasRole(role)) {
directiveBody.render(env.getOut());
} else {
env.getOut().write("");
}
}
private boolean hasRole(Object role) {
String[] roles = role.toString().split(",");
for (String checkRole : roles) {
// TODO 這裏判斷用戶會話是否存在對應的checkRole,如存在則返回true
}
return false;
}
}
步驟3. 爲何要實現預防XSS攻擊?很多情況下我們庫裏的數據存在XSS腳本,導致後臺管理員或普通用戶查看其中包含XSS腳本數據的時候會出現一些惡意的頁面效果,比如盜取用戶會話的cookie sessionid或者是惡意的頁面攻擊效果,或讓你無限彈窗,甚至更嚴重的是導向你到釣魚站點,這對於用戶數據安全和用戶體驗效果都是一種極大的挑戰.
XSS主要是通過頁面JS腳本來執行效果,所以我們使用最常規的辦法轉義我們需要展示的數據,基本可以預防到90%的腳本攻擊
如下例
<#escape x as x?html>
這是一個讀取數據庫的自讀內容: 我是標題內容,請問今天你吃飯了嗎?
<script>alert("我是XSS腳本");</script>
</#escape>
但是頁面很多地方如果都需要展示獨立數據時候我們需要寫太多冗餘代碼,這時候我們可以考慮全局替換,代碼如下
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);
// return new StringReader(templateText);
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
delegate.closeTemplateSource(templateSource);
}
}
步驟4. 我們開始初始化Freemarker配置,並注入使用我們上面所編寫的功能代碼
@Configuration
public class FreeMarkerConfig {
@Autowired(required = false)
private freemarker.template.Configuration configuration;
@Autowired(required = false)
private HasRoleTag hasRoleTag;
@PostConstruct
public void setSharedVariable() {
// 數據轉義
configuration.setTemplateLoader(new HtmlTemplateLoader(configuration.getTemplateLoader()));
// 基本設置
configuration.setNumberFormat("#.####");
configuration.setDateFormat("yyyy-MM-dd");
configuration.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
configuration.setLocale(new Locale("zh_CN"));
configuration.setSharedVariable("hasRole", hasRoleTag);
}
}
總結,全局變量使用轉義標籤,我們頁面不再需要編寫額外的代碼,以普通的${model.content!''}輸出數據即可,但是有的人可能問我那我們使用富文本數據的時候怎麼辦呢,我們不能轉義其中的數據節點,這個情況我們後續會講解另外一種富文本的安全處理
如果喜歡我的教程文章,請點下贊或收藏,如有疑惑可隨時私信或評論區@我,感激不盡