一、表結構設計 二、實現思路 1.系統啟動時將字典數據載入到redis作為可選常量池,以及mapper.xml、dao、數據規則信息載入到redis 2.用訪問時通過springmvc攔截器對用戶進行攔截獲取token然後通過RSA解密獲取用戶信息,將用戶信息,以及請求參數加入本地線程 3.myba ...
一、表結構設計
二、實現思路
1.系統啟動時將字典數據載入到redis作為可選常量池,以及mapper.xml、dao、數據規則信息載入到redis
2.用訪問時通過springmvc攔截器對用戶進行攔截獲取token然後通過RSA解密獲取用戶信息,將用戶信息,以及請求參數加入本地線程
3.mybatis-plus攔截器對mapper攔截然後解析對應dao層介面的方法,用於載入規則配置信息
4、通過策略模式實現讀取不同常量池對規則表達式解析
5、使用jsqlparser實現sql解析註入
6、將處理後的sql交給mybatis-plus框架處理
三、核心代碼:
@Slf4j
@Component
public class DpcInterceptor extends JsqlParserSupport implements InnerInterceptor {
ThreadLocal<String> mapperID = new ThreadLocal<String>();
ThreadLocal<String> methodID = new ThreadLocal<String>();
@Autowired
private RedisService redisService;
@Autowired
private Map<String, AbstractExpressionHandler> expressionHandlers;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
if (!InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
String[] ids = ms.getId().replace(".", ",").split(",");
mapperID.set(ids[ids.length - 2]);
methodID.set(ids[ids.length - 1]);
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(this.parserSingle(mpBs.sql(), ms.getId()));
}
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
Authentication authentication = AuthenticationHolder.get();
try {
if (!Objects.isNull(authentication)) {
Long userId = authentication.getUserId();
String json = redisService.getByKey(String.format("DATA:SCOPE:%s:%s:%s", userId, mapperID.get(), methodID.get()));
if (String_.isNotEmpty(json)) {
Expression where = null;
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
if ((where = plainSelect.getWhere()) == null) {
plainSelect.setWhere(new HexValue("1 = 1"));
}
StringBuffer sbWhere = new StringBuffer();
for (MenuRuleDto menuRule : Json_.toList(json, MenuRuleDto.class)) {
if (1 == menuRule.getType() && 1 == menuRule.getStatus()) {
String sqlWhere = menuRule.getCriteria();
for (String variablePoolName : menuRule.getVariablePool().split(",")) {
String whereSql = expressionHandlers.get(String_.lowerFirst(variablePoolName) + "ExpressionHandler").expression(sqlWhere);
if (whereSql.indexOf("$") < 0 && whereSql.indexOf("::") < 0) {
sbWhere.append(" and (" + whereSql + ") ");
}
}
}
}
plainSelect.setWhere(new AndExpression(new Parenthesis(where),new Column(sbWhere.substring(4).toString())));
}
}
} catch (Exception e) {
log.debug(e.getMessage());
} finally {
methodID.remove();
mapperID.remove();
}
}
@Slf4j
@Component
public class AuthenticationExpressionHandler extends AbstractExpressionHandler{
@Override
public String expression(String whereSql) {
Authentication currenUser = AuthenticationHolder.get();
if (currenUser != null) {
try {
Map<String,String> params = new HashMap<>();
for (Field declaredField : currenUser.getClass().getDeclaredFields()) {
declaredField.setAccessible(true);
if (declaredField.get(currenUser) != null) {
params.put("current"+ String_.upperFirst(declaredField.getName()),declaredField.get(currenUser).toString());
}
}
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
return stringSubstitutor.replace(whereSql);
} catch (Exception e) {
log.debug("[AuthenticationExpressionHandler 參數替換失敗]{}",e.getMessage(),e);
}
}
return whereSql;
}
}
/**
* 預設的表達式解析工具類,鑒權數據,請求數據作為參數
*/
@Component
public class DefaultExpressionHandler extends AbstractExpressionHandler{
@Override
public String expression(String whereSql) {
Map<String,String> params = ThreadLocal_.get("params");
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
return stringSubstitutor.replace(whereSql);
}
}
/**
* 表達式子解析處理介面
*/
public abstract class AbstractExpressionHandler {
public abstract String expression(String whereSql);
}
/**
* 預設的表達式解析工具類,字典數據作為參數
*/
@Component
public class DictionaryExpressionHandler extends AbstractExpressionHandler{
@Autowired
private RedisService redisService;
@Override
public String expression(String whereSql) {
Map<String,String> params = new HashMap<>();
for (String variable : getVariables(whereSql)) {
if (variable.indexOf("::")>0) {
String dictType = variable.substring(0,variable.indexOf("::"));
String dictCode = variable.substring(variable.indexOf("::")+2);
String dictJson = redisService.getByKey(RedisConst.DICT_DATA_KEY + dictType);
List<SysDictData> dictData = Json_.toList(dictJson,SysDictData.class);
for (SysDictData dictDatum : dictData) {
if (dictDatum.getDictLabel().equals(dictCode)) {
params.put(variable,dictDatum.getDictValue());
}
}
}
}
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
return stringSubstitutor.replace(whereSql);
}
public static Set<String> getVariables(String str) {
Set<String> variables = new HashSet<>();
Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
variables.add(matcher.group(1));
}
return variables;
}
}