解决系统切换部署数据库兼容性问题:PostgreSQL权限查询SQL语句报错、MySQL授权不起作用

This commit is contained in:
datagear 2021-03-01 20:53:36 +08:00
parent 161777634b
commit 36eecab78e
10 changed files with 140 additions and 69 deletions

View File

@ -752,32 +752,6 @@ public abstract class AbstractMybatisService<T> extends SqlSessionDaoSupport
return map;
}
/**
* 转义为SQL字符串值
*
* @param s
* @return
*/
protected String escapeForSqlStringValue(String s)
{
if (s == null)
throw new IllegalArgumentException();
StringBuilder sb = new StringBuilder();
for (int i = 0, len = s.length(); i < len; i++)
{
char c = s.charAt(i);
if (c == '\'')
sb.append("''");
else
sb.append(c);
}
return sb.toString();
}
/**
* 获取sql语句的名字空间
*

View File

@ -113,14 +113,16 @@ public class AuthorizationServiceImpl extends AbstractMybatisDataPermissionEntit
if (user.isAdmin())
return Authorization.PERMISSION_MAX;
MbSqlDialect dialect = getDialect();
int unsetPermission = -9;
Map<String, Object> params = buildParamMap();
addDataPermissionParameters(params, user, resourceType, true, false);
params.put(DATA_PERMISSION_PARAM_UNSET_PERMISSION, unsetPermission);
params.put("placeholderId", IDUtil.uuid());
params.put("patternSource", escapeForSqlStringValue(patternSource));
params.put("placeholderId", dialect.toStringLiteral(IDUtil.uuid()));
params.put("patternSource", dialect.toStringLiteral(patternSource));
List<DataIdPermission> dataIdPermissions = selectListMybatis("getDataIdPermissionForPatternSource", params);

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 datagear.tech
*
* Licensed under the LGPLv3 license:
* http://www.gnu.org/licenses/lgpl-3.0.html
*/
package org.datagear.management.util;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.ibatis.type.BooleanTypeHandler;
import org.apache.ibatis.type.JdbcType;
/**
* 使用字面值{@code 'true'}{@code 'false'}存储{@linkplain Boolean}类型的MyBatis类型映射器
* <p>
* {@linkplain BooleanTypeHandler}{@linkplain #setNonNullParameter(PreparedStatement, int, Boolean, JdbcType)}
* 使用的是{@linkplain PreparedStatement#setBoolean(int, boolean)}对于不同数据库的驱动程序当数据库列类型为VARCHAR时
* 存储的值可能为{@code 'true'}{@code 'false'}也可能为{@code '1'}{@code '0'}
* 对于某些需要根据此列值进行判断处理的SQL语句会出现数据库切换兼容问题
* </p>
* <p>
* 所以这里定义此类{@linkplain Boolean}类型统一存储为{@code 'true'}{@code 'false'}要求数据库列类型为VARCHAR
* </p>
*
* @author datagear@163.com
*
*/
public class LiteralBooleanTypeHandler extends BooleanTypeHandler
{
public LiteralBooleanTypeHandler()
{
super();
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
throws SQLException
{
String value = (Boolean.TRUE.equals(parameter) ? "true" : "false");
ps.setString(i, value);
}
}

View File

@ -15,7 +15,7 @@ import org.datagear.management.service.impl.AbstractMybatisService;
* 此类仅用于为{@linkplain AbstractMybatisService}及其实现类提供多数据库部署支持
* </p>
* <p>
* 基本思路是根据当前部署数据库生成底层Mybatis所需的数据库方言SQL片段然后以参数的方式传入底层SQL
* 对于分页基本思路是根据当前部署数据库生成底层Mybatis所需的数据库方言SQL片段然后以参数的方式传入底层SQL
* Mapper语境组装成合规的SQL语句
* </p>
*
@ -47,6 +47,39 @@ public abstract class MbSqlDialect
this.identifierQuote = identifierQuote;
}
/**
* 将字符串转换为SQL字符串字面值
* <p>
* 例如{@code "abc'def"}应转换为{@code "'abc''def'"}
* </p>
*
* @param value
* @return
*/
public String toStringLiteral(String value)
{
if (value == null)
return "NULL";
StringBuilder sb = new StringBuilder();
sb.append('\'');
for (int i = 0, len = value.length(); i < len; i++)
{
char c = value.charAt(i);
if (c == '\'')
sb.append("''");
else
sb.append(c);
}
sb.append('\'');
return sb.toString();
}
/**
* 是否支持分页查询
*

View File

@ -31,6 +31,23 @@ public class PostgresqlMbSqlDialect extends MbSqlDialect
return true;
}
@Override
public String toStringLiteral(String value)
{
String literal = super.toStringLiteral(value);
// PostgreSQL对于
// SELECT 'abc' AS STR_LITERAL
// 格式的SQL语句查询结果中STR_LITERAL是unknown类型这会导致如下类似查询语句
// SELECT * FROM (SELECT 'abc' AS STR_LITERAL) A INNER JOIN SOME_TABLE B
// ON A.STR_LITERAL = B.SOME_VARCHAR_COLUMN
//
// failed to find conversion function from unknown to text
// 错误无法执行在末尾添加::text进行类型转换可解决
return literal + "::text";
}
@Override
public String pagingSqlHead(int index, int fetchSize)
{

View File

@ -12,7 +12,7 @@
VALUES
(
#{entity.id}, #{entity.resource}, #{entity.resourceType}, #{entity.principal}, #{entity.principalType},
#{entity.permission}, #{entity.enabled},#{entity.createUser.id}
#{entity.permission}, #{entity.enabled, jdbcType=VARCHAR},#{entity.createUser.id}
)
</insert>
@ -23,7 +23,7 @@
AUTH_PRINCIPAL = #{entity.principal},
AUTH_PRINCIPAL_TYPE = #{entity.principalType},
AUTH_PERMISSION = #{entity.permission},
AUTH_ENABLED = #{entity.enabled}
AUTH_ENABLED = #{entity.enabled, jdbcType=VARCHAR}
WHERE
AUTH_ID = #{entity.id}
</update>
@ -75,8 +75,8 @@
(
<include refid="commonDataPermission.dataIdPermissionQueryViewHead" />
SELECT
'${placeholderId}' AS DP_AUTH_DATA_ID,
'${patternSource}' AS DP_AUTH_DATA_PATTERN_SRC,
${placeholderId} AS DP_AUTH_DATA_ID,
${patternSource} AS DP_AUTH_DATA_PATTERN_SRC,
PLACEHOLDER_TABLE.PLACEHOLDER_COL
FROM
/*Derby没有类似from dual的语法为了兼容其它数据库这里采用了此种方法*/

View File

@ -10,7 +10,7 @@
)
VALUES
(
#{entity.id}, #{entity.name}, #{entity.description}, #{entity.enabled}
#{entity.id}, #{entity.name}, #{entity.description}, #{entity.enabled, jdbcType=VARCHAR}
)
</insert>
@ -18,7 +18,7 @@
UPDATE DATAGEAR_ROLE SET
ROLE_NAME = #{entity.name},
ROLE_DESCRIPTION = #{entity.description},
ROLE_ENABLED = #{entity.enabled}
ROLE_ENABLED = #{entity.enabled, jdbcType=VARCHAR}
WHERE
ROLE_ID = #{entity.id}
</update>

View File

@ -6,11 +6,13 @@
<insert id="insert">
INSERT INTO DATAGEAR_USER
(
USER_ID, USER_NAME, USER_PASSWORD, USER_REAL_NAME, USER_EMAIL, USER_IS_ADMIN, USER_CREATE_TIME
USER_ID, USER_NAME, USER_PASSWORD, USER_REAL_NAME, USER_EMAIL,
USER_IS_ADMIN, USER_CREATE_TIME
)
VALUES
(
#{entity.id}, #{entity.name}, #{entity.password}, #{entity.realName}, #{entity.email}, #{entity.admin}, #{entity.createTime}
#{entity.id}, #{entity.name}, #{entity.password}, #{entity.realName}, #{entity.email},
#{entity.admin, jdbcType=VARCHAR}, #{entity.createTime}
)
</insert>
@ -22,7 +24,7 @@
</if>
USER_REAL_NAME = #{entity.realName},
USER_EMAIL = #{entity.email},
USER_IS_ADMIN = #{entity.admin}
USER_IS_ADMIN = #{entity.admin, jdbcType=VARCHAR}
WHERE
USER_ID = #{entity.id}
</update>

View File

@ -91,59 +91,54 @@
-->
<sql id="userOnResourceTypePermissionQueryView">
SELECT
DG_AUTH.AUTH_RESOURCE,
DG_AUTH.AUTH_RESOURCE_TYPE,
AUTH.AUTH_RESOURCE,
AUTH.AUTH_RESOURCE_TYPE,
(
/*最高级权限值加权,管理员授权始终高于普通用户授权*/
CASE DG_AUTH.CREATOR_IS_ADMIN
CASE USR.USER_IS_ADMIN
WHEN 'true' THEN
CASE DG_AUTH.AUTH_PRINCIPAL_TYPE
WHEN 'ALL' THEN (5000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'ANONYMOUS' THEN (6000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'ROLE' THEN (7000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'USER' THEN (8000000 + DG_AUTH.AUTH_PERMISSION)
CASE AUTH.AUTH_PRINCIPAL_TYPE
WHEN 'ALL' THEN (5000000 + AUTH.AUTH_PERMISSION)
WHEN 'ANONYMOUS' THEN (6000000 + AUTH.AUTH_PERMISSION)
WHEN 'ROLE' THEN (7000000 + AUTH.AUTH_PERMISSION)
WHEN 'USER' THEN (8000000 + AUTH.AUTH_PERMISSION)
ELSE 0
END
ELSE
CASE DG_AUTH.AUTH_PRINCIPAL_TYPE
WHEN 'ALL' THEN (1000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'ANONYMOUS' THEN (2000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'ROLE' THEN (3000000 + DG_AUTH.AUTH_PERMISSION)
WHEN 'USER' THEN (4000000 + DG_AUTH.AUTH_PERMISSION)
CASE AUTH.AUTH_PRINCIPAL_TYPE
WHEN 'ALL' THEN (1000000 + AUTH.AUTH_PERMISSION)
WHEN 'ANONYMOUS' THEN (2000000 + AUTH.AUTH_PERMISSION)
WHEN 'ROLE' THEN (3000000 + AUTH.AUTH_PERMISSION)
WHEN 'USER' THEN (4000000 + AUTH.AUTH_PERMISSION)
ELSE 0
END
END
) AS AUTH_PRIORITY_PERMISSION
FROM
(
SELECT
A.*,
U.USER_IS_ADMIN AS CREATOR_IS_ADMIN
FROM
DATAGEAR_AUTHORIZATION A,
DATAGEAR_USER U
WHERE
A.AUTH_CREATE_USER_ID = U.USER_ID
) DG_AUTH
DATAGEAR_AUTHORIZATION AUTH
INNER JOIN
DATAGEAR_USER USR
ON
AUTH.AUTH_CREATE_USER_ID = USR.USER_ID
WHERE
DG_AUTH.AUTH_ENABLED = 'true'
AUTH.AUTH_ENABLED = 'true'
AND
(
DG_AUTH.AUTH_RESOURCE_TYPE = '${DP_RESOURCE_TYPE}'
AUTH.AUTH_RESOURCE_TYPE = '${DP_RESOURCE_TYPE}'
<if test='DP_RESOURCE_SUPPORT_PATTERN != null and DP_RESOURCE_SUPPORT_PATTERN == true'>
OR DG_AUTH.AUTH_RESOURCE_TYPE = '${DP_RESOURCE_TYPE}_PATTERN'
OR AUTH.AUTH_RESOURCE_TYPE = '${DP_RESOURCE_TYPE}_PATTERN'
</if>
)
AND
(
DG_AUTH.AUTH_PRINCIPAL_TYPE = 'ALL'
AUTH.AUTH_PRINCIPAL_TYPE = 'ALL'
<choose><when test="DP_CURRENT_USER.anonymous == true">
OR DG_AUTH.AUTH_PRINCIPAL_TYPE = 'ANONYMOUS'
OR AUTH.AUTH_PRINCIPAL_TYPE = 'ANONYMOUS'
</when><otherwise>
OR
(
DG_AUTH.AUTH_PRINCIPAL_TYPE = 'ROLE'
AND DG_AUTH.AUTH_PRINCIPAL IN
AUTH.AUTH_PRINCIPAL_TYPE = 'ROLE'
AND AUTH.AUTH_PRINCIPAL IN
(
SELECT
RO.ROLE_ID
@ -157,7 +152,7 @@
RU.RU_USER_ID = '${DP_CURRENT_USER.id}' AND RO.ROLE_ENABLED = 'true'
)
)
OR (DG_AUTH.AUTH_PRINCIPAL_TYPE = 'USER' AND DG_AUTH.AUTH_PRINCIPAL = '${DP_CURRENT_USER.id}')
OR (AUTH.AUTH_PRINCIPAL_TYPE = 'USER' AND AUTH.AUTH_PRINCIPAL = '${DP_CURRENT_USER.id}')
</otherwise></choose>
)
</sql>

View File

@ -17,6 +17,7 @@ import java.util.List;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.datagear.analysis.TemplateDashboardWidgetResManager;
import org.datagear.analysis.support.FileTemplateDashboardWidgetResManager;
import org.datagear.analysis.support.NameAsTemplateDashboardWidgetResManager;
@ -70,6 +71,7 @@ import org.datagear.management.service.impl.SchemaServiceImpl;
import org.datagear.management.service.impl.SqlHistoryServiceImpl;
import org.datagear.management.service.impl.UserPasswordEncoder;
import org.datagear.management.service.impl.UserServiceImpl;
import org.datagear.management.util.LiteralBooleanTypeHandler;
import org.datagear.management.util.dialect.MbSqlDialect;
import org.datagear.management.util.dialect.MbSqlDialectBuilder;
import org.datagear.meta.resolver.DBMetaResolver;
@ -325,6 +327,7 @@ public class CoreConfig implements InitializingBean
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(this.dataSourceConfig.dataSource());
bean.setMapperLocations(mapperResources);
bean.setTypeHandlers(new TypeHandler<?>[] { new LiteralBooleanTypeHandler() });
return bean.getObject();
}
catch (Exception e)