mirror of
https://github.com/chillzhuang/blade-tool
synced 2024-11-13 14:09:29 +08:00
🎉 3.2.0.RELEASE 新增灵活数据权限特性
This commit is contained in:
parent
08187b2e1f
commit
3468b0f694
13
README.md
13
README.md
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Cloud-2020-blue.svg" alt="Coverage Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.5.2-blue.svg" alt="Downloads">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.5.6-blue.svg" alt="Downloads">
|
||||
</p>
|
||||
|
||||
## SpringBlade微服务开发平台
|
||||
@ -45,7 +45,8 @@ blade-tool
|
||||
* 交流二群:`751253339`(满)
|
||||
* 交流三群:`784729540`(满)
|
||||
* 交流四群:`1034621754`(满)
|
||||
* 交流五群:`946350912`
|
||||
* 交流五群:`946350912`(满)
|
||||
* 交流六群: `511624269`
|
||||
|
||||
## 在线演示
|
||||
* Sword演示地址:[https://sword.bladex.vip](https://sword.bladex.vip)
|
||||
@ -166,10 +167,4 @@ LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求
|
||||
## 鸣谢
|
||||
* mica([Mica](https://github.com/lets-mica/mica))
|
||||
* 如梦技术([DreamLu](https://www.dreamlu.net/))
|
||||
* pigx([Pig Microservice](https://www.pig4cloud.com/zh-cn/))
|
||||
* avue([avue](https://avue.top/))
|
||||
* gitee.ltd([gitee.ltd](https://gitee.ltd/))
|
||||
* 鲸宵(<a href="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/jx.png" target="_blank">鲸宵</a>)
|
||||
|
||||
## 关注我们
|
||||
![](https://images.gitee.com/uploads/images/2019/0330/065148_f0ada806_410595.jpeg)
|
||||
* avue([avue](https://avuejs.com/))
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.tenant;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 租户拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BladeTenantInterceptor extends TenantLineInnerInterceptor {
|
||||
|
||||
/**
|
||||
* 租户处理器
|
||||
*/
|
||||
private TenantLineHandler tenantLineHandler;
|
||||
|
||||
@Override
|
||||
public void setTenantLineHandler(TenantLineHandler tenantLineHandler) {
|
||||
super.setTenantLineHandler(tenantLineHandler);
|
||||
this.tenantLineHandler = tenantLineHandler;
|
||||
}
|
||||
|
||||
}
|
@ -16,13 +16,15 @@
|
||||
package org.springblade.core.boot.tenant;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.config.MybatisPlusConfiguration;
|
||||
import org.springblade.core.mp.config.MybatisPlusConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* 多租户配置类
|
||||
@ -36,19 +38,29 @@ import org.springframework.context.annotation.Configuration;
|
||||
public class TenantConfiguration {
|
||||
|
||||
/**
|
||||
* 多租户配置类
|
||||
*/
|
||||
private final BladeTenantProperties properties;
|
||||
|
||||
/**
|
||||
* 自定义租户处理器
|
||||
* 自定义多租户处理器
|
||||
*
|
||||
* @param tenantProperties 多租户配置类
|
||||
* @return TenantHandler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TenantLineHandler.class)
|
||||
public TenantLineHandler bladeTenantHandler() {
|
||||
return new BladeTenantHandler(properties);
|
||||
@Primary
|
||||
public TenantLineHandler bladeTenantHandler(BladeTenantProperties tenantProperties) {
|
||||
return new BladeTenantHandler(tenantProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义租户拦截器
|
||||
*
|
||||
* @param tenantHandler 多租户处理器
|
||||
* @return BladeTenantInterceptor
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantLineHandler tenantHandler) {
|
||||
BladeTenantInterceptor tenantInterceptor = new BladeTenantInterceptor();
|
||||
tenantInterceptor.setTenantLineHandler(tenantHandler);
|
||||
return tenantInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +101,7 @@ mybatis-plus:
|
||||
swagger:
|
||||
title: SpringBlade 接口文档系统
|
||||
description: SpringBlade 接口文档系统
|
||||
version: 3.1.0
|
||||
version: 3.2.0
|
||||
license: Powered By SpringBlade
|
||||
licenseUrl: https://bladex.vip
|
||||
terms-of-service-url: https://bladex.vip
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
37
blade-core-datascope/pom.xml
Normal file
37
blade-core-datascope/pom.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>blade-core-datascope</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${blade.tool.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!--Mybatis-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-mybatis</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!--Jdbc-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>tomcat-jdbc</artifactId>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.annotation;
|
||||
|
||||
import org.springblade.core.datascope.constant.DataScopeConstant;
|
||||
import org.springblade.core.datascope.enums.DataScopeEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限定义
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface DataAuth {
|
||||
|
||||
/**
|
||||
* 资源编号
|
||||
*/
|
||||
String code() default "";
|
||||
|
||||
/**
|
||||
* 数据权限对应字段
|
||||
*/
|
||||
String column() default DataScopeConstant.DEFAULT_COLUMN;
|
||||
|
||||
/**
|
||||
* 数据权限规则
|
||||
*/
|
||||
DataScopeEnum type() default DataScopeEnum.ALL;
|
||||
|
||||
/**
|
||||
* 可见字段
|
||||
*/
|
||||
String field() default "*";
|
||||
|
||||
/**
|
||||
* 数据权限规则值域
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.datascope.handler.BladeDataScopeHandler;
|
||||
import org.springblade.core.datascope.handler.BladeScopeModelHandler;
|
||||
import org.springblade.core.datascope.handler.DataScopeHandler;
|
||||
import org.springblade.core.datascope.handler.ScopeModelHandler;
|
||||
import org.springblade.core.datascope.interceptor.DataScopeInterceptor;
|
||||
import org.springblade.core.datascope.props.DataScopeProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* 数据权限配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties(DataScopeProperties.class)
|
||||
public class DataScopeConfiguration {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ScopeModelHandler.class)
|
||||
public ScopeModelHandler scopeModelHandler() {
|
||||
return new BladeScopeModelHandler(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ScopeModelHandler.class)
|
||||
@ConditionalOnMissingBean(DataScopeHandler.class)
|
||||
public DataScopeHandler dataScopeHandler(ScopeModelHandler scopeModelHandler) {
|
||||
return new BladeDataScopeHandler(scopeModelHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(DataScopeHandler.class)
|
||||
@ConditionalOnMissingBean(DataScopeInterceptor.class)
|
||||
public DataScopeInterceptor interceptor(DataScopeHandler dataScopeHandler, DataScopeProperties dataScopeProperties) {
|
||||
return new DataScopeInterceptor(dataScopeHandler, dataScopeProperties);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.constant;
|
||||
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
/**
|
||||
* 数据权限常量
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface DataScopeConstant {
|
||||
|
||||
String DEFAULT_COLUMN = "create_dept";
|
||||
|
||||
/**
|
||||
* 获取部门数据
|
||||
*/
|
||||
String DATA_BY_DEPT = "select id from blade_dept where ancestors like concat(concat('%', ?),'%') and is_deleted = 0";
|
||||
|
||||
/**
|
||||
* 根据resourceCode获取数据权限配置
|
||||
*/
|
||||
String DATA_BY_CODE = "select resource_code, scope_column, scope_field, scope_type, scope_value from blade_scope_data where resource_code = ?";
|
||||
|
||||
/**
|
||||
* 根据mapperId获取数据权限配置
|
||||
*
|
||||
* @param size 数量
|
||||
* @return String
|
||||
*/
|
||||
static String dataByMapper(int size) {
|
||||
return "select resource_code, scope_column, scope_field, scope_type, scope_value from blade_scope_data where scope_class = ? and id in (select scope_id from blade_role_scope where role_id in (" + buildHolder(size) + "))";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Sql占位符
|
||||
*
|
||||
* @param size 数量
|
||||
* @return String
|
||||
*/
|
||||
static String buildHolder(int size) {
|
||||
StringBuilder builder = StringUtil.builder();
|
||||
for (int i = 0; i < size; i++) {
|
||||
builder.append("?,");
|
||||
}
|
||||
return StringUtil.removeSuffix(builder.toString(), ",");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据权限类型
|
||||
*
|
||||
* @author lengleng, Chill
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataScopeEnum {
|
||||
/**
|
||||
* 全部数据
|
||||
*/
|
||||
ALL(1, "全部"),
|
||||
|
||||
/**
|
||||
* 本人可见
|
||||
*/
|
||||
OWN(2, "本人可见"),
|
||||
|
||||
/**
|
||||
* 所在机构可见
|
||||
*/
|
||||
OWN_DEPT(3, "所在机构可见"),
|
||||
|
||||
/**
|
||||
* 所在机构及子级可见
|
||||
*/
|
||||
OWN_DEPT_CHILD(4, "所在机构及子级可见"),
|
||||
|
||||
/**
|
||||
* 自定义
|
||||
*/
|
||||
CUSTOM(5, "自定义");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final int type;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
public static DataScopeEnum of(Integer dataScopeType) {
|
||||
if (dataScopeType == null) {
|
||||
return null;
|
||||
}
|
||||
DataScopeEnum[] values = DataScopeEnum.values();
|
||||
for (DataScopeEnum scopeTypeEnum : values) {
|
||||
if (scopeTypeEnum.type == dataScopeType) {
|
||||
return scopeTypeEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.exception;
|
||||
|
||||
/**
|
||||
* 数据权限异常
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DataScopeException extends RuntimeException {
|
||||
|
||||
public DataScopeException() {
|
||||
}
|
||||
|
||||
public DataScopeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DataScopeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.datascope.enums.DataScopeEnum;
|
||||
import org.springblade.core.datascope.model.DataScopeModel;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.tool.constant.RoleConstant;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.PlaceholderUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 默认数据权限规则
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BladeDataScopeHandler implements DataScopeHandler {
|
||||
|
||||
private final ScopeModelHandler scopeModelHandler;
|
||||
|
||||
@Override
|
||||
public String sqlCondition(String mapperId, DataScopeModel dataScope, BladeUser bladeUser, String originalSql) {
|
||||
|
||||
//数据权限资源编号
|
||||
String code = dataScope.getResourceCode();
|
||||
|
||||
//根据mapperId从数据库中获取对应模型
|
||||
DataScopeModel dataScopeDb = scopeModelHandler.getDataScopeByMapper(mapperId, bladeUser.getRoleId());
|
||||
|
||||
//mapperId配置未取到则从数据库中根据资源编号获取
|
||||
if (dataScopeDb == null && StringUtil.isNotBlank(code)) {
|
||||
dataScopeDb = scopeModelHandler.getDataScopeByCode(code);
|
||||
}
|
||||
|
||||
//未从数据库找到对应配置则采用默认
|
||||
dataScope = (dataScopeDb != null) ? dataScopeDb : dataScope;
|
||||
|
||||
//判断数据权限类型并组装对应Sql
|
||||
Integer scopeRule = Objects.requireNonNull(dataScope).getScopeType();
|
||||
DataScopeEnum scopeTypeEnum = DataScopeEnum.of(scopeRule);
|
||||
List<Long> ids = new ArrayList<>();
|
||||
String whereSql = "where scope.{} in ({})";
|
||||
if (DataScopeEnum.ALL == scopeTypeEnum || StringUtil.containsAny(bladeUser.getRoleName(), RoleConstant.ADMIN)) {
|
||||
return null;
|
||||
} else if (DataScopeEnum.CUSTOM == scopeTypeEnum) {
|
||||
whereSql = PlaceholderUtil.getDefaultResolver().resolveByMap(dataScope.getScopeValue(), BeanUtil.toMap(bladeUser));
|
||||
} else if (DataScopeEnum.OWN == scopeTypeEnum) {
|
||||
ids.add(bladeUser.getUserId());
|
||||
} else if (DataScopeEnum.OWN_DEPT == scopeTypeEnum) {
|
||||
ids.addAll(Func.toLongList(bladeUser.getDeptId()));
|
||||
} else if (DataScopeEnum.OWN_DEPT_CHILD == scopeTypeEnum) {
|
||||
List<Long> deptIds = Func.toLongList(bladeUser.getDeptId());
|
||||
ids.addAll(deptIds);
|
||||
deptIds.forEach(deptId -> {
|
||||
List<Long> deptIdList = scopeModelHandler.getDeptAncestors(deptId);
|
||||
ids.addAll(deptIdList);
|
||||
});
|
||||
}
|
||||
return StringUtil.format("select {} from ({}) scope " + whereSql, Func.toStr(dataScope.getScopeField(), "*"), originalSql, dataScope.getScopeColumn(), StringUtil.join(ids));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.datascope.constant.DataScopeConstant;
|
||||
import org.springblade.core.datascope.model.DataScopeModel;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.tool.utils.CacheUtil.SYS_CACHE;
|
||||
|
||||
|
||||
/**
|
||||
* BladeScopeModelHandler
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BladeScopeModelHandler implements ScopeModelHandler {
|
||||
|
||||
private static final String SCOPE_CACHE_CODE = "dataScope:code:";
|
||||
private static final String SCOPE_CACHE_CLASS = "dataScope:class:";
|
||||
private static final String DEPT_CACHE_ANCESTORS = "dept:ancestors:";
|
||||
private static final DataScopeModel SEARCHED_DATA_SCOPE_MODEL = new DataScopeModel(Boolean.TRUE);
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 获取数据权限
|
||||
*
|
||||
* @param mapperId 数据权限mapperId
|
||||
* @param roleId 用户角色集合
|
||||
* @return DataScopeModel
|
||||
*/
|
||||
@Override
|
||||
public DataScopeModel getDataScopeByMapper(String mapperId, String roleId) {
|
||||
List<Object> args = new ArrayList<>(Collections.singletonList(mapperId));
|
||||
List<Long> roleIds = Func.toLongList(roleId);
|
||||
args.addAll(roleIds);
|
||||
// 增加searched字段防止未配置的参数重复读库导致缓存击穿
|
||||
// 后续若有新增配置则会清空缓存重新加载
|
||||
DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, DataScopeModel.class);
|
||||
if (dataScope == null || !dataScope.getSearched()) {
|
||||
List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.dataByMapper(roleIds.size()), args.toArray(), new BeanPropertyRowMapper<>(DataScopeModel.class));
|
||||
if (CollectionUtil.isNotEmpty(list)) {
|
||||
dataScope = list.iterator().next();
|
||||
dataScope.setSearched(Boolean.TRUE);
|
||||
} else {
|
||||
dataScope = SEARCHED_DATA_SCOPE_MODEL;
|
||||
}
|
||||
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CLASS, mapperId + StringPool.COLON + roleId, dataScope);
|
||||
}
|
||||
return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限
|
||||
*
|
||||
* @param code 数据权限资源编号
|
||||
* @return DataScopeModel
|
||||
*/
|
||||
@Override
|
||||
public DataScopeModel getDataScopeByCode(String code) {
|
||||
DataScopeModel dataScope = CacheUtil.get(SYS_CACHE, SCOPE_CACHE_CODE, code, DataScopeModel.class);
|
||||
// 增加searched字段防止未配置的参数重复读库导致缓存击穿
|
||||
// 后续若有新增配置则会清空缓存重新加载
|
||||
if (dataScope == null || !dataScope.getSearched()) {
|
||||
List<DataScopeModel> list = jdbcTemplate.query(DataScopeConstant.DATA_BY_CODE, new Object[]{code}, new BeanPropertyRowMapper<>(DataScopeModel.class));
|
||||
if (CollectionUtil.isNotEmpty(list)) {
|
||||
dataScope = list.iterator().next();
|
||||
dataScope.setSearched(Boolean.TRUE);
|
||||
} else {
|
||||
dataScope = SEARCHED_DATA_SCOPE_MODEL;
|
||||
}
|
||||
CacheUtil.put(SYS_CACHE, SCOPE_CACHE_CODE, code, dataScope);
|
||||
}
|
||||
return StringUtil.isNotBlank(dataScope.getResourceCode()) ? dataScope : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门子级
|
||||
*
|
||||
* @param deptId 部门id
|
||||
* @return deptIds
|
||||
*/
|
||||
@Override
|
||||
public List<Long> getDeptAncestors(Long deptId) {
|
||||
List ancestors = CacheUtil.get(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, List.class);
|
||||
if (CollectionUtil.isEmpty(ancestors)) {
|
||||
ancestors = jdbcTemplate.queryForList(DataScopeConstant.DATA_BY_DEPT, new Object[]{deptId}, Long.class);
|
||||
CacheUtil.put(SYS_CACHE, DEPT_CACHE_ANCESTORS, deptId, ancestors);
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.handler;
|
||||
|
||||
import org.springblade.core.datascope.model.DataScopeModel;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
|
||||
/**
|
||||
* 数据权限规则
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface DataScopeHandler {
|
||||
|
||||
/**
|
||||
* 获取过滤sql
|
||||
*
|
||||
* @param mapperId 数据查询类
|
||||
* @param dataScope 数据权限类
|
||||
* @param bladeUser 当前用户信息
|
||||
* @param originalSql 原始Sql
|
||||
* @return sql
|
||||
*/
|
||||
String sqlCondition(String mapperId, DataScopeModel dataScope, BladeUser bladeUser, String originalSql);
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.handler;
|
||||
|
||||
import org.springblade.core.datascope.model.DataScopeModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 获取数据权限模型统一接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface ScopeModelHandler {
|
||||
|
||||
/**
|
||||
* 获取数据权限
|
||||
*
|
||||
* @param mapperId 数据权限mapperId
|
||||
* @param roleId 用户角色集合
|
||||
* @return DataScopeModel
|
||||
*/
|
||||
DataScopeModel getDataScopeByMapper(String mapperId, String roleId);
|
||||
|
||||
/**
|
||||
* 获取数据权限
|
||||
*
|
||||
* @param code 数据权限资源编号
|
||||
* @return DataScopeModel
|
||||
*/
|
||||
DataScopeModel getDataScopeByCode(String code);
|
||||
|
||||
/**
|
||||
* 获取部门子级
|
||||
*
|
||||
* @param deptId 部门id
|
||||
* @return deptIds
|
||||
*/
|
||||
List<Long> getDeptAncestors(Long deptId);
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.interceptor;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.mapping.StatementType;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.springblade.core.datascope.annotation.DataAuth;
|
||||
import org.springblade.core.datascope.handler.DataScopeHandler;
|
||||
import org.springblade.core.datascope.model.DataScopeModel;
|
||||
import org.springblade.core.datascope.props.DataScopeProperties;
|
||||
import org.springblade.core.mp.intercept.QueryInterceptor;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
|
||||
/**
|
||||
* mybatis 数据权限拦截器
|
||||
*
|
||||
* @author L.cm, Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class DataScopeInterceptor implements QueryInterceptor {
|
||||
|
||||
private final ConcurrentMap<String, DataAuth> dataAuthMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
private final DataScopeHandler dataScopeHandler;
|
||||
private final DataScopeProperties dataScopeProperties;
|
||||
|
||||
@Override
|
||||
public void intercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
//未启用则放行
|
||||
if (!dataScopeProperties.getEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//未取到用户则放行
|
||||
BladeUser bladeUser = SecureUtil.getUser();
|
||||
if (bladeUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SqlCommandType.SELECT != ms.getSqlCommandType() || StatementType.CALLABLE == ms.getStatementType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String originalSql = boundSql.getSql();
|
||||
|
||||
//查找注解中包含DataAuth类型的参数
|
||||
DataAuth dataAuth = findDataAuthAnnotation(ms);
|
||||
|
||||
//注解为空并且数据权限方法名未匹配到,则放行
|
||||
String mapperId = ms.getId();
|
||||
String className = mapperId.substring(0, mapperId.lastIndexOf(StringPool.DOT));
|
||||
String mapperName = ClassUtil.getShortName(className);
|
||||
String methodName = mapperId.substring(mapperId.lastIndexOf(StringPool.DOT) + 1);
|
||||
boolean mapperSkip = dataScopeProperties.getMapperKey().stream().noneMatch(methodName::contains)
|
||||
|| dataScopeProperties.getMapperExclude().stream().anyMatch(mapperName::contains);
|
||||
if (dataAuth == null && mapperSkip) {
|
||||
return;
|
||||
}
|
||||
|
||||
//创建数据权限模型
|
||||
DataScopeModel dataScope = new DataScopeModel();
|
||||
|
||||
//若注解不为空,则配置注解项
|
||||
if (dataAuth != null) {
|
||||
dataScope.setResourceCode(dataAuth.code());
|
||||
dataScope.setScopeColumn(dataAuth.column());
|
||||
dataScope.setScopeType(dataAuth.type().getType());
|
||||
dataScope.setScopeField(dataAuth.field());
|
||||
dataScope.setScopeValue(dataAuth.value());
|
||||
}
|
||||
|
||||
//获取数据权限规则对应的筛选Sql
|
||||
String sqlCondition = dataScopeHandler.sqlCondition(mapperId, dataScope, bladeUser, originalSql);
|
||||
if (!StringUtil.isBlank(sqlCondition)) {
|
||||
PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
|
||||
mpBoundSql.sql(sqlCondition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限注解信息
|
||||
*
|
||||
* @param mappedStatement mappedStatement
|
||||
* @return DataAuth
|
||||
*/
|
||||
private DataAuth findDataAuthAnnotation(MappedStatement mappedStatement) {
|
||||
String id = mappedStatement.getId();
|
||||
return dataAuthMap.computeIfAbsent(id, (key) -> {
|
||||
String className = key.substring(0, key.lastIndexOf(StringPool.DOT));
|
||||
String mapperBean = StringUtil.firstCharToLower(ClassUtil.getShortName(className));
|
||||
Object mapper = SpringUtil.getBean(mapperBean);
|
||||
String methodName = key.substring(key.lastIndexOf(StringPool.DOT) + 1);
|
||||
Class<?>[] interfaces = ClassUtil.getAllInterfaces(mapper);
|
||||
for (Class<?> mapperInterface : interfaces) {
|
||||
for (Method method : mapperInterface.getDeclaredMethods()) {
|
||||
if (methodName.equals(method.getName()) && method.isAnnotationPresent(DataAuth.class)) {
|
||||
return method.getAnnotation(DataAuth.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springblade.core.datascope.constant.DataScopeConstant;
|
||||
import org.springblade.core.datascope.enums.DataScopeEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据权限实体类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class DataScopeModel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造器创建
|
||||
*/
|
||||
public DataScopeModel(Boolean searched) {
|
||||
this.searched = searched;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已查询
|
||||
*/
|
||||
private Boolean searched = Boolean.FALSE;
|
||||
/**
|
||||
* 资源编号
|
||||
*/
|
||||
private String resourceCode;
|
||||
/**
|
||||
* 数据权限字段
|
||||
*/
|
||||
private String scopeColumn = DataScopeConstant.DEFAULT_COLUMN;
|
||||
/**
|
||||
* 数据权限规则
|
||||
*/
|
||||
private Integer scopeType = DataScopeEnum.ALL.getType();
|
||||
/**
|
||||
* 可见字段
|
||||
*/
|
||||
private String scopeField;
|
||||
/**
|
||||
* 数据权限规则值
|
||||
*/
|
||||
private String scopeValue;
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.datascope.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据权限参数配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "blade.data-scope")
|
||||
public class DataScopeProperties {
|
||||
|
||||
/**
|
||||
* 开启数据权限
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
/**
|
||||
* mapper方法匹配关键字
|
||||
*/
|
||||
private List<String> mapperKey = Arrays.asList("page", "Page", "list", "List");
|
||||
|
||||
/**
|
||||
* mapper过滤
|
||||
*/
|
||||
private List<String> mapperExclude = Collections.singletonList("FlowMapper");
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -25,7 +25,7 @@ public interface AppConstant {
|
||||
/**
|
||||
* 应用版本
|
||||
*/
|
||||
String APPLICATION_VERSION = "3.1.0";
|
||||
String APPLICATION_VERSION = "3.2.0";
|
||||
|
||||
/**
|
||||
* 基础包
|
||||
|
@ -33,6 +33,7 @@ public interface TokenConstant {
|
||||
String ACCOUNT = "account";
|
||||
String USER_ID = "user_id";
|
||||
String ROLE_ID = "role_id";
|
||||
String DEPT_ID = "dept_id";
|
||||
String USER_NAME = "user_name";
|
||||
String ROLE_NAME = "role_name";
|
||||
String TENANT_ID = "tenant_id";
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -13,21 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.config;
|
||||
package org.springblade.core.mp.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springblade.core.boot.props.MybatisPlusProperties;
|
||||
import org.springblade.core.mp.intercept.QueryInterceptor;
|
||||
import org.springblade.core.mp.plugins.BladePaginationInterceptor;
|
||||
import org.springblade.core.mp.plugins.SqlLogInterceptor;
|
||||
import org.springblade.core.mp.props.MybatisPlusProperties;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.ObjectUtil;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
/**
|
||||
* mybatisplus 配置
|
||||
@ -40,22 +49,48 @@ import org.springframework.context.annotation.Configuration;
|
||||
@EnableConfigurationProperties(MybatisPlusProperties.class)
|
||||
public class MybatisPlusConfiguration {
|
||||
|
||||
private final TenantLineHandler tenantLineHandler;
|
||||
|
||||
/**
|
||||
* 租户拦截器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TenantLineInnerInterceptor.class)
|
||||
public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
|
||||
return new TenantLineInnerInterceptor(new TenantLineHandler() {
|
||||
@Override
|
||||
public Expression getTenantId() {
|
||||
return new StringValue(Func.toStr(SecureUtil.getTenantId(), BladeConstant.ADMIN_TENANT_ID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* mybatis-plus 拦截器集合
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor(MybatisPlusProperties mybatisPlusProperties) {
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor(ObjectProvider<QueryInterceptor[]> queryInterceptors,
|
||||
TenantLineInnerInterceptor tenantLineInnerInterceptor,
|
||||
MybatisPlusProperties mybatisPlusProperties) {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 配置租户拦截器
|
||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));
|
||||
interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
|
||||
// 配置分页拦截器
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
|
||||
paginationInnerInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
|
||||
paginationInnerInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
|
||||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||
BladePaginationInterceptor paginationInterceptor = new BladePaginationInterceptor();
|
||||
// 配置自定义查询拦截器
|
||||
QueryInterceptor[] queryInterceptorArray = queryInterceptors.getIfAvailable();
|
||||
if (ObjectUtil.isNotEmpty(queryInterceptorArray)) {
|
||||
AnnotationAwareOrderComparator.sort(queryInterceptorArray);
|
||||
paginationInterceptor.setQueryInterceptors(queryInterceptorArray);
|
||||
}
|
||||
paginationInterceptor.setMaxLimit(mybatisPlusProperties.getPageLimit());
|
||||
paginationInterceptor.setOverflow(mybatisPlusProperties.getOverflow());
|
||||
interceptor.addInnerInterceptor(paginationInterceptor);
|
||||
return interceptor;
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, DreamLu All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: DreamLu 卢春梦 (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.mp.intercept;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* 自定义 mybatis plus 查询拦截器
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public interface QueryInterceptor extends Ordered {
|
||||
|
||||
/**
|
||||
* 拦截处理
|
||||
*
|
||||
* @param executor
|
||||
* @param ms
|
||||
* @param parameter
|
||||
* @param rowBounds
|
||||
* @param resultHandler
|
||||
* @param boundSql
|
||||
*/
|
||||
void intercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql);
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@Override
|
||||
default int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.mp.plugins;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.springblade.core.mp.intercept.QueryInterceptor;
|
||||
|
||||
/**
|
||||
* 拓展分页拦截器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Setter
|
||||
public class BladePaginationInterceptor extends PaginationInnerInterceptor {
|
||||
|
||||
/**
|
||||
* 查询拦截器
|
||||
*/
|
||||
private QueryInterceptor[] queryInterceptors;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
QueryInterceptorExecutor.exec(queryInterceptors, executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
return super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.mp.plugins;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.springblade.core.mp.intercept.QueryInterceptor;
|
||||
import org.springblade.core.tool.utils.ObjectUtil;
|
||||
|
||||
/**
|
||||
* 查询拦截器执行器
|
||||
*
|
||||
* <p>
|
||||
* 目的:抽取此方法是为了后期方便同步更新 {@link BladePaginationInterceptor}
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public class QueryInterceptorExecutor {
|
||||
|
||||
/**
|
||||
* 执行查询拦截器
|
||||
*/
|
||||
static void exec(QueryInterceptor[] interceptors, Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws Throwable {
|
||||
if (ObjectUtil.isEmpty(interceptors)) {
|
||||
return;
|
||||
}
|
||||
for (QueryInterceptor interceptor : interceptors) {
|
||||
interceptor.intercept(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.props;
|
||||
package org.springblade.core.mp.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -45,6 +45,11 @@ public class BladeUser implements Serializable {
|
||||
*/
|
||||
@ApiModelProperty(hidden = true)
|
||||
private String tenantId;
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
@ApiModelProperty(hidden = true)
|
||||
private String deptId;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
|
@ -48,6 +48,7 @@ public class SecureUtil {
|
||||
private final static String ACCOUNT = TokenConstant.ACCOUNT;
|
||||
private final static String USER_ID = TokenConstant.USER_ID;
|
||||
private final static String ROLE_ID = TokenConstant.ROLE_ID;
|
||||
private final static String DEPT_ID = TokenConstant.DEPT_ID;
|
||||
private final static String USER_NAME = TokenConstant.USER_NAME;
|
||||
private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
|
||||
private final static String TENANT_ID = TokenConstant.TENANT_ID;
|
||||
@ -98,6 +99,7 @@ public class SecureUtil {
|
||||
Long userId = Func.toLong(claims.get(SecureUtil.USER_ID));
|
||||
String tenantId = Func.toStr(claims.get(SecureUtil.TENANT_ID));
|
||||
String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
|
||||
String deptId = Func.toStr(claims.get(SecureUtil.DEPT_ID));
|
||||
String account = Func.toStr(claims.get(SecureUtil.ACCOUNT));
|
||||
String roleName = Func.toStr(claims.get(SecureUtil.ROLE_NAME));
|
||||
String userName = Func.toStr(claims.get(SecureUtil.USER_NAME));
|
||||
@ -107,6 +109,7 @@ public class SecureUtil {
|
||||
bladeUser.setTenantId(tenantId);
|
||||
bladeUser.setAccount(account);
|
||||
bladeUser.setRoleId(roleId);
|
||||
bladeUser.setDeptId(deptId);
|
||||
bladeUser.setRoleName(roleName);
|
||||
bladeUser.setUserName(userName);
|
||||
return bladeUser;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -55,7 +55,7 @@ public class SwaggerProperties {
|
||||
/**
|
||||
* 版本
|
||||
**/
|
||||
private String version = "3.1.0";
|
||||
private String version = "3.2.0";
|
||||
/**
|
||||
* 许可证
|
||||
**/
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -44,6 +44,8 @@ public class XssConfiguration {
|
||||
|
||||
/**
|
||||
* 防XSS注入
|
||||
*
|
||||
* @return FilterRegistrationBean
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
|
||||
|
@ -65,6 +65,11 @@ public interface BladeConstant {
|
||||
int DB_ADMIN_NON_LOCKED = 0;
|
||||
int DB_ADMIN_LOCKED = 1;
|
||||
|
||||
/**
|
||||
* 顶级父节点id
|
||||
*/
|
||||
Long TOP_PARENT_ID = 0L;
|
||||
|
||||
/**
|
||||
* 管理员对应的租户ID
|
||||
*/
|
||||
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: Chill 庄骞 (smallchill@163.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* 缓存工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class CacheUtil {
|
||||
|
||||
public static final String SYS_CACHE = "blade:sys";
|
||||
|
||||
private static CacheManager cacheManager;
|
||||
|
||||
/**
|
||||
* 获取缓存工具
|
||||
*
|
||||
* @return CacheManager
|
||||
*/
|
||||
private static CacheManager getCacheManager() {
|
||||
if (cacheManager == null) {
|
||||
cacheManager = SpringUtil.getBean(CacheManager.class);
|
||||
}
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存对象
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @return Cache
|
||||
*/
|
||||
public static Cache getCache(String cacheName) {
|
||||
return getCacheManager().getCache(cacheName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @param keyPrefix 缓存键前缀
|
||||
* @param key 缓存键值
|
||||
* @return Cache
|
||||
*/
|
||||
@Nullable
|
||||
public static Object get(String cacheName, String keyPrefix, Object key) {
|
||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
||||
return null;
|
||||
}
|
||||
return getCache(cacheName).get(keyPrefix.concat(String.valueOf(key))).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @param keyPrefix 缓存键前缀
|
||||
* @param key 缓存键值
|
||||
* @param type 转换类型
|
||||
* @param <T> 类型
|
||||
* @return Cache
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T get(String cacheName, String keyPrefix, Object key, @Nullable Class<T> type) {
|
||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
||||
return null;
|
||||
}
|
||||
return getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @param keyPrefix 缓存键前缀
|
||||
* @param key 缓存键值
|
||||
* @param valueLoader 重载对象
|
||||
* @param <T> 类型
|
||||
* @return Cache
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T get(String cacheName, String keyPrefix, Object key, Callable<T> valueLoader) {
|
||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Cache.ValueWrapper valueWrapper = getCache(cacheName).get(keyPrefix.concat(String.valueOf(key)));
|
||||
Object value = null;
|
||||
if (valueWrapper == null) {
|
||||
T call = valueLoader.call();
|
||||
if (Func.isNotEmpty(call)) {
|
||||
getCache(cacheName).put(keyPrefix.concat(String.valueOf(key)), call);
|
||||
value = call;
|
||||
}
|
||||
} else {
|
||||
value = valueWrapper.get();
|
||||
}
|
||||
return (T) value;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @param keyPrefix 缓存键前缀
|
||||
* @param key 缓存键值
|
||||
* @param value 缓存值
|
||||
*/
|
||||
public static void put(String cacheName, String keyPrefix, Object key, @Nullable Object value) {
|
||||
getCache(cacheName).put(keyPrefix.concat(String.valueOf(key)), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
* @param keyPrefix 缓存键前缀
|
||||
* @param key 缓存键值
|
||||
*/
|
||||
public static void evict(String cacheName, String keyPrefix, Object key) {
|
||||
if (Func.hasEmpty(cacheName, keyPrefix, key)) {
|
||||
return;
|
||||
}
|
||||
getCache(cacheName).evict(keyPrefix.concat(String.valueOf(key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*
|
||||
* @param cacheName 缓存名
|
||||
*/
|
||||
public static void clear(String cacheName) {
|
||||
if (Func.isEmpty(cacheName)) {
|
||||
return;
|
||||
}
|
||||
getCache(cacheName).clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -405,6 +405,7 @@ public class DateUtil {
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 表达式
|
||||
* @param query 移动查询
|
||||
* @return 时间
|
||||
*/
|
||||
public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
|
||||
@ -453,6 +454,9 @@ public class DateUtil {
|
||||
|
||||
/**
|
||||
* Converts local date time to Calendar.
|
||||
*
|
||||
* @param localDateTime LocalDateTime
|
||||
* @return Calendar
|
||||
*/
|
||||
public static Calendar toCalendar(final LocalDateTime localDateTime) {
|
||||
return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
|
||||
|
@ -0,0 +1,143 @@
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 占位符解析器
|
||||
*
|
||||
* @author meilin.huang, chill
|
||||
*/
|
||||
public class PlaceholderUtil {
|
||||
/**
|
||||
* 默认前缀占位符
|
||||
*/
|
||||
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
|
||||
|
||||
/**
|
||||
* 默认后缀占位符
|
||||
*/
|
||||
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
|
||||
|
||||
/**
|
||||
* 默认单例解析器
|
||||
*/
|
||||
private static final PlaceholderUtil DEFAULT_RESOLVER = new PlaceholderUtil();
|
||||
|
||||
/**
|
||||
* 占位符前缀
|
||||
*/
|
||||
private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
|
||||
|
||||
/**
|
||||
* 占位符后缀
|
||||
*/
|
||||
private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
|
||||
|
||||
|
||||
private PlaceholderUtil() {
|
||||
}
|
||||
|
||||
private PlaceholderUtil(String placeholderPrefix, String placeholderSuffix) {
|
||||
this.placeholderPrefix = placeholderPrefix;
|
||||
this.placeholderSuffix = placeholderSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的占位符解析器,即占位符前缀为"${", 后缀为"}"
|
||||
*
|
||||
* @return PlaceholderUtil
|
||||
*/
|
||||
public static PlaceholderUtil getDefaultResolver() {
|
||||
return DEFAULT_RESOLVER;
|
||||
}
|
||||
|
||||
public static PlaceholderUtil getResolver(String placeholderPrefix, String placeholderSuffix) {
|
||||
return new PlaceholderUtil(placeholderPrefix, placeholderSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}
|
||||
*
|
||||
* @param content 要解析的带有占位符的模板字符串
|
||||
* @param values 按照模板占位符索引位置设置对应的值
|
||||
* @return {String}
|
||||
*/
|
||||
public String resolve(String content, String... values) {
|
||||
int start = content.indexOf(this.placeholderPrefix);
|
||||
if (start == -1) {
|
||||
return content;
|
||||
}
|
||||
//值索引
|
||||
int valueIndex = 0;
|
||||
StringBuilder result = new StringBuilder(content);
|
||||
while (start != -1) {
|
||||
int end = result.indexOf(this.placeholderSuffix);
|
||||
String replaceContent = values[valueIndex++];
|
||||
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
|
||||
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}
|
||||
*
|
||||
* @param content 要解析的带有占位符的模板字符串
|
||||
* @param values 按照模板占位符索引位置设置对应的值
|
||||
* @return {String}
|
||||
*/
|
||||
public String resolve(String content, Object[] values) {
|
||||
return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据替换规则来替换指定模板中的占位符值
|
||||
*
|
||||
* @param content 要解析的字符串
|
||||
* @param rule 解析规则回调
|
||||
* @return {String}
|
||||
*/
|
||||
public String resolveByRule(String content, Function<String, String> rule) {
|
||||
int start = content.indexOf(this.placeholderPrefix);
|
||||
if (start == -1) {
|
||||
return content;
|
||||
}
|
||||
StringBuilder result = new StringBuilder(content);
|
||||
while (start != -1) {
|
||||
int end = result.indexOf(this.placeholderSuffix, start + 1);
|
||||
//获取占位符属性值,如${id}, 即获取id
|
||||
String placeholder = result.substring(start + this.placeholderPrefix.length(), end);
|
||||
//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
|
||||
String replaceContent = placeholder.trim().isEmpty() ? "" : rule.apply(placeholder);
|
||||
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
|
||||
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换模板中占位符内容,占位符的内容即为map key对应的值,key为占位符中的内容
|
||||
*
|
||||
* @param content 模板内容
|
||||
* @param valueMap 值映射
|
||||
* @return 替换完成后的字符串
|
||||
*/
|
||||
public String resolveByMap(String content, final Map<String, Object> valueMap) {
|
||||
return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据properties文件替换占位符内容
|
||||
*
|
||||
* @param content 模板内容
|
||||
* @param properties 配置
|
||||
* @return {String}
|
||||
*/
|
||||
public String resolveByProperties(String content, final Properties properties) {
|
||||
return resolveByRule(content, properties::getProperty);
|
||||
}
|
||||
|
||||
}
|
@ -1438,5 +1438,40 @@ public class StringUtil extends org.springframework.util.StringUtils {
|
||||
return sb.toString().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 首字母变小写
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String firstCharToLower(String str) {
|
||||
char firstChar = str.charAt(0);
|
||||
if (firstChar >= CharPool.UPPER_A && firstChar <= CharPool.UPPER_Z) {
|
||||
char[] arr = str.toCharArray();
|
||||
arr[0] += (CharPool.LOWER_A - CharPool.UPPER_A);
|
||||
return new String(arr);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 首字母变大写
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String firstCharToUpper(String str) {
|
||||
char firstChar = str.charAt(0);
|
||||
if (firstChar >= CharPool.LOWER_A && firstChar <= CharPool.LOWER_Z) {
|
||||
char[] arr = str.toCharArray();
|
||||
arr[0] -= (CharPool.LOWER_A - CharPool.UPPER_A);
|
||||
return new String(arr);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
23
pom.xml
23
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>blade-tool</name>
|
||||
<description>
|
||||
@ -36,26 +36,26 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<blade.tool.version>3.1.0</blade.tool.version>
|
||||
<blade.tool.version>3.2.0</blade.tool.version>
|
||||
|
||||
<java.version>1.8</java.version>
|
||||
<maven.plugin.version>3.8.0</maven.plugin.version>
|
||||
<maven.plugin.version>3.8.1</maven.plugin.version>
|
||||
<swagger.version>2.10.5</swagger.version>
|
||||
<swagger.models.version>1.6.2</swagger.models.version>
|
||||
<knife4j.version>2.0.8</knife4j.version>
|
||||
<mybatis.plus.version>3.4.3.1</mybatis.plus.version>
|
||||
<knife4j.version>2.0.9</knife4j.version>
|
||||
<mybatis.plus.version>3.4.3.4</mybatis.plus.version>
|
||||
<mybatis.plus.generator.version>3.4.1</mybatis.plus.generator.version>
|
||||
<protostuff.version>1.6.0</protostuff.version>
|
||||
<disruptor.version>3.4.2</disruptor.version>
|
||||
<spring.boot.admin.version>2.3.1</spring.boot.admin.version>
|
||||
<spring.boot.admin.version>2.5.3</spring.boot.admin.version>
|
||||
<mica.auto.version>1.2.5</mica.auto.version>
|
||||
<alibaba.cloud.version>2021.1</alibaba.cloud.version>
|
||||
<alibaba.nacos.version>2.0.2</alibaba.nacos.version>
|
||||
<alibaba.nacos.version>2.0.3</alibaba.nacos.version>
|
||||
<alibaba.seata.version>1.4.2</alibaba.seata.version>
|
||||
<spring.plugin.version>2.0.0.RELEASE</spring.plugin.version>
|
||||
|
||||
<spring.boot.version>2.5.2</spring.boot.version>
|
||||
<spring.cloud.version>2020.0.3</spring.cloud.version>
|
||||
<spring.boot.version>2.5.6</spring.boot.version>
|
||||
<spring.cloud.version>2020.0.4</spring.cloud.version>
|
||||
<spring.platform.version>Cairo-SR8</spring.platform.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@ -77,6 +77,7 @@
|
||||
<module>blade-core-oss</module>
|
||||
<module>blade-core-transaction</module>
|
||||
<module>blade-core-report</module>
|
||||
<module>blade-core-datascope</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -278,7 +279,7 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Nexus -->
|
||||
<plugin>
|
||||
<!--<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.8</version>
|
||||
@ -288,7 +289,7 @@
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
|
Loading…
Reference in New Issue
Block a user