|
|
@@ -0,0 +1,67 @@
|
|
|
+package com.fs.common.utils.security;
|
|
|
+
|
|
|
+import com.fs.common.exception.ServiceException;
|
|
|
+import com.fs.common.utils.StringUtils;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+/**
|
|
|
+ * SQL 动态片段安全校验工具(排序方向、列名白名单)
|
|
|
+ */
|
|
|
+public final class SqlSafeUtils {
|
|
|
+
|
|
|
+ private static final Set<String> ORDER_DIRECTIONS = new HashSet<String>(Arrays.asList("asc", "desc"));
|
|
|
+
|
|
|
+ /** 直播趋势统计允许的 category 列名 */
|
|
|
+ private static final Set<String> LIVE_CATEGORY_COLUMNS = new HashSet<String>(Arrays.asList(
|
|
|
+ "page_views", "unique_visitors", "total_views", "unique_viewers", "streams"
|
|
|
+ ));
|
|
|
+
|
|
|
+ private SqlSafeUtils() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验排序方向,仅允许 asc/desc;空值直接返回
|
|
|
+ */
|
|
|
+ public static String validateOrderDirection(String order) {
|
|
|
+ if (StringUtils.isEmpty(order)) {
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+ String lower = order.trim().toLowerCase(Locale.ROOT);
|
|
|
+ if (!ORDER_DIRECTIONS.contains(lower)) {
|
|
|
+ throw new ServiceException("排序方向非法,仅支持 asc/desc");
|
|
|
+ }
|
|
|
+ return lower;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验直播趋势 category,防止 ${category} SQL 注入
|
|
|
+ */
|
|
|
+ public static String validateLiveCategory(String category) {
|
|
|
+ if (StringUtils.isEmpty(category)) {
|
|
|
+ throw new ServiceException("category 不能为空");
|
|
|
+ }
|
|
|
+ String value = category.trim();
|
|
|
+ if (!LIVE_CATEGORY_COLUMNS.contains(value)) {
|
|
|
+ throw new ServiceException("非法的 category 参数");
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验排序字段名:仅允许字母数字下划线,且长度受限
|
|
|
+ */
|
|
|
+ public static String validateColumnName(String column) {
|
|
|
+ if (StringUtils.isEmpty(column)) {
|
|
|
+ throw new ServiceException("排序字段不能为空");
|
|
|
+ }
|
|
|
+ String value = column.trim();
|
|
|
+ if (value.length() > 64 || !value.matches("^[a-zA-Z0-9_]+$")) {
|
|
|
+ throw new ServiceException("排序字段名非法: " + column);
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+}
|