diff --git a/docs/zh/core/mask.md b/docs/zh/core/mask.md index b6a568a5..a61c0075 100644 --- a/docs/zh/core/mask.md +++ b/docs/zh/core/mask.md @@ -49,7 +49,7 @@ MaskManager.registerMaskProcesser("自定义规则名称" ``` 2、使用自定义的脱敏规则 -```java 7 +```java @Table("tb_account") public class Account { @@ -66,19 +66,47 @@ public class Account { 在某些场景下,程序希望查询得到的数据是原始数据,而非脱敏数据。比如要去查询用户的手机号,然后给用户发送短信。又或者说,我们进入编辑页面编辑用户数据, 如果编辑页面展示的是脱敏数据,然后再次点击保存,那么数据库的真实数据也会被脱敏覆盖。 -因此,MaskManager 提供了 `skipMask`、`restoreMask` 两个方法来处理这种场景: +因此,MaskManager 提供了 `withoutMask`、`skipMask`、`restoreMask` 三个方法来处理这种场景: -```java 2,7 +推荐使用`withoutMask`方法,该方法使用了模版方法设计模式,保障跳过脱敏处理并执行相关逻辑后自动恢复脱敏处理。 + +`withoutMask`方法实现如下: + +```java +public static T withoutMask(Supplier supplier) { + try { + skipMask(); + return supplier.get(); + } finally { + restoreMask(); + } +} +``` + +使用方法: + +```java +AccountMapper mapper = ...; +List accounts = MaskManager.withoutMask(mapper::selectAll); +System.out.println(accounts); +``` + +`skipMask`和`restoreMask`方法需配套使用,推荐使用`try{...}finally{...}`模式,如下例所示。 +使用这两个方法可以自主控制跳过脱敏处理和恢复脱敏处理的时机。 +当跳过脱敏处理和恢复脱敏处理无法放在同一个方法中时,可以使用这两个方法。 +此时需要仔细处理代码分支及异常,以防止跳过脱敏处理后未恢复脱敏处理,导致安全隐患。 + +```java try { - MaskManager.skipMask() + MaskManager.skipMask(); //此处查询到的数据不会进行脱敏处理 - accountMapper.selectListByQuery(...) + accountMapper.selectListByQuery(...); } finally { - MaskManager.restoreMask() + MaskManager.restoreMask(); } ``` ::: tip 提示 -在具体的应用中,我们通常会把 `skipMask()` 和 `restoreMask()` 放到统一的拦截器里,对某一类业务进行统一拦截和处理。 +在具体的应用中,我们通常会把`withoutMask`、`skipMask()` 和 `restoreMask()` 放到统一的拦截器里,对某一类业务进行统一拦截和处理。 ::: diff --git a/docs/zh/core/multi-tenancy.md b/docs/zh/core/multi-tenancy.md index fd40953a..5dc4235c 100644 --- a/docs/zh/core/multi-tenancy.md +++ b/docs/zh/core/multi-tenancy.md @@ -111,16 +111,42 @@ DELETE FROM tb_article where id = ? and tenant_id in (?, ?, ?) ### 忽略租户条件 -在某些场景下,在增删改查等操作,我们可能需要忽略租户条件,此时可以使用如下代码: +在某些场景下,在增删改查等操作,我们可能需要忽略租户条件, +此时可以使用TenantManager的`withoutTenantCondition`、`ignoreTenantCondition`、`restoreTenantCondition`三个方法。 + +推荐使用`withoutTenantCondition`方法,该方法使用了模版方法设计模式,保障忽略 tenant 条件并执行相关逻辑后自动恢复 tenant 条件。 + +`withoutTenantCondition`方法实现如下: +```java +public static T withoutTenantCondition(Supplier supplier) { + try { + ignoreTenantCondition(); + return supplier.get(); + } finally { + restoreTenantCondition(); + } +} +``` +使用方法: +```java +TenantAccountMapper mapper = ...; +List tenantAccounts = TenantManager.withoutTenantCondition(mapper::selectAll); +System.out.println(tenantAccounts); +``` + +`ignoreTenantCondition`和`restoreTenantCondition`方法需配套使用,推荐使用`try{...}finally{...}`模式,如下例所示。 +使用这两个方法可以自主控制忽略 tenant 条件和恢复 tenant 条件的时机。 +当忽略 tenant 条件和恢复 tenant 条件无法放在同一个方法中时,可以使用这两个方法。 +此时需要仔细处理代码分支及异常,以防止忽略 tenant 条件后未恢复 tenant 条件,导致数据异常。 ```java try { - TenantManager.ignoreTenantCondition() + TenantManager.ignoreTenantCondition(); //此处操作的数据不会带有 tenant_id 的条件 - accountMapper.selectListByQuery(...) + accountMapper.selectListByQuery(...); } finally { - TenantManager.restoreTenantCondition() + TenantManager.restoreTenantCondition(); } ``` diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mask/MaskManager.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mask/MaskManager.java index 5d0e0f4a..c4736eb6 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mask/MaskManager.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mask/MaskManager.java @@ -17,6 +17,7 @@ package com.mybatisflex.core.mask; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** * 数据脱敏工厂类 @@ -56,6 +57,18 @@ public class MaskManager { private static ThreadLocal skipFlags = new ThreadLocal<>(); + /** + * 跳过脱敏处理 + */ + public static T withoutMask(Supplier supplier) { + try { + skipMask(); + return supplier.get(); + } finally { + restoreMask(); + } + } + /** * 跳过脱敏处理 */ diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/tenant/TenantManager.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/tenant/TenantManager.java index b2d677eb..81886412 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/tenant/TenantManager.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/tenant/TenantManager.java @@ -15,6 +15,8 @@ */ package com.mybatisflex.core.tenant; +import java.util.function.Supplier; + public class TenantManager { private static ThreadLocal ignoreFlags = new ThreadLocal<>(); @@ -29,6 +31,18 @@ public class TenantManager { TenantManager.tenantFactory = tenantFactory; } + /** + * 忽略 tenant 条件 + */ + public static T withoutTenantCondition(Supplier supplier) { + try { + ignoreTenantCondition(); + return supplier.get(); + } finally { + restoreTenantCondition(); + } + } + /** * 忽略 tenant 条件 diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MaskManagerTest.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MaskManagerTest.java new file mode 100644 index 00000000..39cd6c57 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MaskManagerTest.java @@ -0,0 +1,33 @@ +package com.mybatisflex.test; + +import com.mybatisflex.core.MybatisFlexBootstrap; +import com.mybatisflex.core.mask.MaskManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.util.List; + +public class MaskManagerTest { + + public static void main(String[] args) { + DataSource dataSource = new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.H2) + .setName("db1") + .addScript("schema.sql") + .addScript("data.sql") + .build(); + + MybatisFlexBootstrap.getInstance() + .setDataSource(dataSource) + .addMapper(AccountMapper.class) + .start(); + + //获取 mapper + AccountMapper mapper = MybatisFlexBootstrap.getInstance().getMapper(AccountMapper.class); + List accounts = mapper.selectAll(); +// List accounts = MaskManager.withoutMask(mapper::selectAll); + System.out.println(accounts); + } + +} diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantAccount.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantAccount.java index 8d664d46..3d8d6339 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantAccount.java +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantAccount.java @@ -59,4 +59,15 @@ public class TenantAccount{ public void setTenantId(Long tenantId) { this.tenantId = tenantId; } + + @Override + public String toString() { + return "TenantAccount{" + + "id=" + id + + ", userName='" + userName + '\'' + + ", age=" + age + + ", birthday=" + birthday + + ", tenantId=" + tenantId + + '}'; + } } diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantManagerTester.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantManagerTester.java new file mode 100644 index 00000000..1ce6fc6b --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/TenantManagerTester.java @@ -0,0 +1,46 @@ +package com.mybatisflex.test; + +import com.mybatisflex.core.MybatisFlexBootstrap; +import com.mybatisflex.core.audit.AuditManager; +import com.mybatisflex.core.audit.ConsoleMessageCollector; +import com.mybatisflex.core.tenant.TenantFactory; +import com.mybatisflex.core.tenant.TenantManager; +import com.mybatisflex.mapper.TenantAccountMapper; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +import javax.sql.DataSource; +import java.util.List; + +public class TenantManagerTester { + + public static void main(String[] args) { + DataSource dataSource = new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseType.H2) + .addScript("schema03.sql") + .addScript("data03.sql") + .build(); + + MybatisFlexBootstrap.getInstance() + .setDataSource(dataSource) + .addMapper(TenantAccountMapper.class) + .start(); + + //输出日志 + AuditManager.setAuditEnable(true); + AuditManager.setMessageCollector(new ConsoleMessageCollector()); + + //配置 tenantFactory + TenantManager.setTenantFactory(new TenantFactory() { + @Override + public Object[] getTenantIds() { + return new Object[]{1}; + } + }); + + TenantAccountMapper mapper = MybatisFlexBootstrap.getInstance().getMapper(TenantAccountMapper.class); + List tenantAccounts = TenantManager.withoutTenantCondition(mapper::selectAll); + System.out.println(tenantAccounts); + } + +}