Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ public void clearDataAuthorizationFilters() {
*/
public <T> T columnAuthorization(SQLExecuteState interceptState, String tableName, String columnName, T value) {
if (interceptState != null && interceptState.hasIntercept()) {
String realTableName = interceptState.getTableName(tableName);
String realColumnName = interceptState.getColumnName(tableName,columnName);
String[] resolved = interceptState.resolveTableNameAndColumn(tableName, columnName);
String realTableName = resolved[0];
String realColumnName = resolved[1];

for (DataAuthorizationFilter filter : filters) {
if (filter.supportColumnAuthorization(realTableName, realColumnName, value)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.codingapi.springboot.authorization.enhancer;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* 派生表(FROM/JOIN 子查询)输出列到物理表字段的投影映射。
* <p>
* 解决 Issue #212:分页包装 SQL(SELECT 裸列 FROM (...) AS __base__)执行后,
* 结果集元数据中的表名是派生表别名(或空串),无法直接映射回物理表字段。
* 通过记录派生表每一列的来源(显式列引用 / 唯一 * 展开),实现结果列归因。
*/
class DerivedTableProjection {

/**
* 显式投影列:key = 输出列名(小写),value = [物理表名, 物理字段名]
*/
private final Map<String, String[]> explicitColumns = new HashMap<>();

/**
* 表达式/函数 AS 别名等非物理列派生出的输出列(小写),不参与 * 归因
*/
private final Set<String> expressionColumns = new HashSet<>();

/**
* 唯一 * 来源的物理表名;为 null 且 starAmbiguous 为 false 时表示无 * 投影
*/
private String starTable;

/**
* * 来源是否歧义(多个 * 或多表 SELECT *),歧义时不做 * 归因
*/
private boolean starAmbiguous;

/**
* 添加显式投影列
*
* @param columnKey 输出列名(小写)
* @param tableName 物理表名
* @param columnName 物理字段名
*/
void putExplicit(String columnKey, String tableName, String columnName) {
explicitColumns.put(columnKey, new String[]{tableName, columnName});
}

/**
* 标记非物理列(表达式 AS 别名)
*
* @param columnKey 输出列名(小写)
*/
void markExpression(String columnKey) {
expressionColumns.add(columnKey);
}

/**
* 添加 * 展开来源的物理表;出现多个来源时视为歧义
*
* @param tableName 物理表名,null 表示来源不可解析
*/
void addStarTable(String tableName) {
if (tableName == null || starTable != null) {
starTable = null;
starAmbiguous = true;
} else {
starTable = tableName;
}
}

/**
* 标记 * 来源歧义
*/
void markStarAmbiguous() {
starTable = null;
starAmbiguous = true;
}

/**
* * 展开来源的物理表名(链式解析 t.* 来源时使用)
*/
String getStarTable() {
return starTable;
}

/**
* * 来源是否歧义(链式解析 t.* 来源时使用)
*/
boolean isStarAmbiguous() {
return starAmbiguous;
}

/**
* 将输出列解析为物理表字段
*
* @param columnKey 输出列名(小写)
* @param columnName 输出列名(原样,用于 * 展开归因)
* @return [物理表名, 物理字段名],无法归因时返回 null
*/
String[] find(String columnKey, String columnName) {
String[] reference = explicitColumns.get(columnKey);
if (reference != null) {
return reference;
}
if (expressionColumns.contains(columnKey)) {
return null;
}
if (starTable != null) {
return new String[]{starTable, columnName};
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ public class TableColumnAliasContext {

private final Map<String, String> columnAliasMap;

/**
* 派生表投影:key = 派生表别名(小写)
*/
private final Map<String, DerivedTableProjection> derivedTables;

protected TableColumnAliasContext() {
this.columnAliases = new ArrayList<>();
this.tableAlias = new HashMap<>();
this.columnAliasMap = new HashMap<>();
this.derivedTables = new HashMap<>();
}

/**
Expand All @@ -33,6 +39,106 @@ protected void addTable(String tableAlias, String tableName) {
this.tableAlias.put(tableAlias, tableName);
}

/**
* 注册派生表投影
* @param alias 派生表别名
* @param projection 投影映射
*/
protected void addDerivedTable(String alias, DerivedTableProjection projection) {
if (alias != null && projection != null) {
derivedTables.put(alias.toLowerCase(), projection);
}
}

/**
* 按表引用名(表名或别名,忽略大小写)获取物理表名
* @param tableRef 表引用名
* @return 物理表名,未找到返回 null
*/
protected String resolvePhysicalTable(String tableRef) {
if (tableRef == null || tableRef.isEmpty()) {
return null;
}
for (Map.Entry<String, String> entry : tableAlias.entrySet()) {
if (entry.getKey().equalsIgnoreCase(tableRef)) {
return entry.getValue();
}
}
for (String tableName : tableAlias.values()) {
if (tableName.equalsIgnoreCase(tableRef)) {
return tableName;
}
}
return null;
}

/**
* 获取派生表投影(忽略大小写)
* @param tableRef 派生表别名
* @return 投影映射,未找到返回 null
*/
protected DerivedTableProjection getDerivedTable(String tableRef) {
if (tableRef == null || tableRef.isEmpty()) {
return null;
}
return derivedTables.get(tableRef.toLowerCase());
}

/**
* 联合解析结果集列的(表名, 字段名)到物理表字段。
* <p>
* 元数据表名命中派生表别名、或为空/未知(部分数据库对派生表列上报空表名,
* 见 Issue #212)时,沿派生表投影归因;否则回退到既有解析行为。
*
* @param tableName 元数据表名(或别名)
* @param columnName 元数据字段名(或别名)
* @return [物理表名, 物理字段名]
*/
public String[] resolveTableNameAndColumn(String tableName, String columnName) {
String[] derived = resolveDerivedColumn(tableName, columnName);
if (derived != null) {
return derived;
}
return new String[]{getTableName(tableName), getColumnName(tableName, columnName)};
}

private String[] resolveDerivedColumn(String tableName, String columnName) {
if (derivedTables.isEmpty() || columnName == null || columnName.isEmpty()) {
return null;
}
String columnKey = columnName.toLowerCase();
String tableKey = tableName == null ? "" : tableName.toLowerCase();
if (!tableKey.isEmpty()) {
DerivedTableProjection projection = derivedTables.get(tableKey);
if (projection != null) {
return projection.find(columnKey, columnName);
}
// 已知物理表/别名:维持原有解析,不做派生归因,避免误匹配
for (String alias : tableAlias.keySet()) {
if (alias.equalsIgnoreCase(tableKey)) {
return null;
}
}
for (String physicalTable : tableAlias.values()) {
if (physicalTable.equalsIgnoreCase(tableKey)) {
return null;
}
}
}
// 表名为空或未知:在所有派生表投影中做唯一匹配(匹配到多个不同物理表时不归因)
String[] matched = null;
for (DerivedTableProjection projection : derivedTables.values()) {
String[] reference = projection.find(columnKey, columnName);
if (reference != null) {
if (matched != null && !matched[0].equalsIgnoreCase(reference[0])) {
return null;
}
matched = reference;
}
}
return matched;
}

/**
* 添加字段别名
* @param parent 父级(上级别名)
Expand Down
Loading
Loading