首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 實用工具類精選(二):特殊字符轉義與方法入參斷言防禦性編程

     編輯:Java 相關知識

Spring 框架內建大量生產級工具類,脫離容器即可獨立使用。本文聚焦 Web 安全核心領域——特殊字符轉義與方法契約防禦,深度解析 HtmlUtils、JavaScriptUtils、StringEscapeUtils、Assert 四大工具類的實戰應用。

特殊字符轉義:Web 安全第一道防線

HTML 特殊字符轉義 —— HtmlUtils

動態數據直接輸出到 HTML 會破壞文檔結構,甚至引發 XSS。


// 危險示例
String userName = "";  // 截斷表格結構
String address = "\" type=\"button";      // 劫持 input 類型

// HtmlUtils 三種轉義模式
String special = "
test1;test2
"; // 1. 標準實體引用(推薦) HtmlUtils.htmlEscape(special); // <div id="testDiv">test1;test2</div> // 2. 十進制數字實體 HtmlUtils.htmlEscapeDecimal(special); // <div id="testDiv">test1;test2</div> // 3. 十六進制數字實體 HtmlUtils.htmlEscapeHex(special); // <div id="testDiv">test1;test2</div> // 反轉義(三種模式通用) HtmlUtils.htmlUnescape(escaped); // 還原原始字符串

JavaScript 特殊字符轉義 —— JavaScriptUtils

動態拼接 JS 代碼時,未轉義字符串會被注入為可執行腳本。


// JSP 片段
List<String> list = Arrays.asList("\";alert();j=\"");  // 攜帶 JS 代碼

// 危險:直接拼接
txtList[0] = "<%=list.get(0)%>";  
// 輸出:txtList[0] = "";alert();j="";  → 彈窗執行

// 安全:JavaScriptUtils 轉義
txtList[0] = "<%=JavaScriptUtils.javaScriptEscape(list.get(0))%>";
// 輸出:txtList[0] = "\";alert();j=\"";  → 純字符串字面量

轉義規則:' → \'、" → \"、\ → \\、\f \n \t \r \b 對應控制字符轉義。

SQL 特殊字符轉義 —— StringEscapeUtils (Apache Commons Lang)

Spring 未提供 SQL 轉義,推薦引入 commons-lang3 使用 StringEscapeUtils,功能更全面。


String userName = "1' or '1'='1";  // 經典注入 Payload
String password = "123456";

userName = StringEscapeUtils.escapeSql(userName);  // 1\' or \'1\'=\'1
password = StringEscapeUtils.escapeSql(password);

String sql = "SELECT * FROM t_user WHERE user='" + userName + "' AND pwd='" + password + "'";
// 生成安全 SQL,單引號被轉義,注入失效

最佳實踐:優先使用 PreparedStatement 參數綁定,從根源消除拼接風險;僅在無法使用參數化查詢的舊代碼/動態 DDL 場景下用轉義兜底。

方法入參斷言:防禦性編程的標準化表達

手寫 if (arg == null) throw ... 碎片化、易漏、可讀性差。Spring org.springframework.util.Assert 提供流式、語義化的契約聲明。

核心斷言方法速查表

方法契約語義異常類型
notNull(obj, msg)參數必須非 nullIllegalArgumentException
isNull(obj, msg)參數必須為 null
isTrue(expr, msg)布爾表達式必須為 true
notEmpty(coll, msg)集合/Map/數組必須含元素
hasLength(str, msg)字符串非 null 且 length > 0
hasText(str, msg)字符串非 null 且含非空白字符 (推薦)
isInstanceOf(clazz, obj, msg)對象必須可轉型為指定類
isAssignable(super, sub, msg)sub 必須是 super 的子類/實現類
notNull(obj) / hasText(str)無消息版,默認異常信息

實戰對比


// 手寫版:冗長、易漏邊界
public InputStream getData(String file) {
    if (file == null || file.length() == 0 || file.trim().isEmpty()) {
        throw new IllegalArgumentException("file 參數無效");
    }
    // ...
}

// Assert 版:一行聲明契約,語義精確
public InputStream getData(String file) {
    Assert.hasText(file, "file 參數必須為非空白字符串");
    // ...
}

進階用法:狀態檢查而非參數檢查


// 檢查對象內部狀態
public void withdraw(Account account, BigDecimal amount) {
    Assert.state(account.isActive(), "賬戶已凍結");
    Assert.state(account.getBalance().compareTo(amount) >= 0, "餘額不足");
    // ...
}

Assert.state() 拋出 IllegalStateException,語義區分「調用者傳參錯誤」與「對象狀態不滿足前置條件」。

工具類選型決策樹


需要轉義?
├─ HTML 輸出上下文 → HtmlUtils.htmlEscape()
├─ JavaScript 字面量上下文 → JavaScriptUtils.javaScriptEscape()
├─ SQL 拼接上下文(無法用 PreparedStatement) → StringEscapeUtils.escapeSql()
└─ 統一入口、全覆蓋、可接受第三方依賴 → StringEscapeUtils (commons-lang3) ✅ 推薦

方法入參驗證?
├─ Spring 專案 / 可引入 spring-core → Assert ✅ 推薦
├─ 非 Spring、追求零依賴 → Objects.requireNonNull / 自定義微型斷言庫
└─ Bean Validation (JSR-380) → @NotNull @Size 等註解(聲明式,運行時需 Provider)

總結

HtmlUtils/JavaScriptUtils 解決輸出編碼問題,StringEscapeUtils 覆蓋全上下文轉義,Assert 落地契約式防禦。這四個工具類體積小、零依賴(除 Commons Lang)、API 穩定,是構建安全、健壯 Java 應用的基石組件。建議將其納入團隊編碼規約「必用清單」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved