feat: 增加通用数据权限控制,通过@DataScope@DataScopes注解方式进行数据权限控制 #(258)

This commit is contained in:
qiufeng
2024-04-29 22:59:36 +08:00
parent 6bb386a32a
commit 123bc6572b
49 changed files with 752 additions and 204 deletions
@@ -38,6 +38,11 @@ public class RemoteOrgFallback implements FallbackFactory<RemoteOrgService> {
public Result<PageResult<UserInfo>> queryUserByCode(String origin, String code, String account, String nickname, String phone, ReqPage reqPage) {
return Result.fail("错误:根据编码获取组织下用户失败" + cause.getMessage());
}
@Override
public Result<List<String>> getOrgIdsByFixCode(String origin, String tenantId, String codes, String direction) {
return Result.fail("错误:根据固定编码获取组织id失败" + cause.getMessage());
}
};
}
}
@@ -0,0 +1,22 @@
package cn.com.mfish.common.oauth.api.fallback;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.api.remote.RemoteRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* @description: 角色远程调用失败处理
* @author: mfish
* @date: 2024/4/29
*/
@Slf4j
@Component
public class RemoteRoleFallback implements FallbackFactory<RemoteRoleService> {
@Override
public RemoteRoleService create(Throwable cause) {
log.error("角色服务调用失败:{}", cause.getMessage());
return (origin, tenantId, codes) -> Result.fail("错误:获取角色id失败" + cause.getMessage());
}
}
@@ -55,6 +55,11 @@ public class RemoteUserFallback implements FallbackFactory<RemoteUserService> {
public Result<List<SsoOrg>> getOrgs(String origin, String userId, String direction) {
return Result.fail("错误:获取用户组织失败" + cause.getMessage());
}
@Override
public Result<List<String>> getOrgIds(String origin, String userId, String tenantId, String direction) {
return Result.fail("错误:获取用户组织ID失败" + cause.getMessage());
}
};
}
}
@@ -33,4 +33,7 @@ public interface RemoteOrgService {
@GetMapping("/org/user/{code}")
Result<PageResult<UserInfo>> queryUserByCode(@RequestHeader(RPCConstants.REQ_ORIGIN) String origin, @PathVariable("code") String code, @RequestParam("account") String account, @RequestParam("nickname") String nickname, @RequestParam("phone") String phone, @SpringQueryMap ReqPage reqPage);
@GetMapping("/org/ids")
Result<List<String>> getOrgIdsByFixCode(@RequestHeader(RPCConstants.REQ_ORIGIN) String origin, @RequestParam("tenantId") String tenantId, @RequestParam("codes") String codes, @RequestParam("direction") String direction);
}
@@ -0,0 +1,24 @@
package cn.com.mfish.common.oauth.api.remote;
import cn.com.mfish.common.core.constants.RPCConstants;
import cn.com.mfish.common.core.constants.ServiceConstants;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.api.fallback.RemoteRoleFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* @description: 角色远程接口
* @author: mfish
* @date: 2024/4/29
*/
@FeignClient(contextId = "remoteRoleService", value = ServiceConstants.OAUTH_SERVICE, fallbackFactory = RemoteRoleFallback.class)
public interface RemoteRoleService {
@GetMapping("/ids/{codes}")
Result<List<String>> getRoleIdsByCode(@RequestHeader(RPCConstants.REQ_ORIGIN) String origin, @RequestParam("tenantId") String tenantId, @RequestParam("codes") String codes);
}
@@ -80,4 +80,16 @@ public interface RemoteUserService {
*/
@GetMapping("/user/orgs/{userId}")
Result<List<SsoOrg>> getOrgs(@RequestHeader(RPCConstants.REQ_ORIGIN) String origin, @PathVariable("userId") String userId, @RequestParam("direction") String direction);
/**
* 获取用户相关组织id
*
* @param origin 来源
* @param userId 用户id
* @param tenantId 租户id
* @param direction 查询方向
* @return
*/
@GetMapping("/orgIds/{userId}")
Result<List<String>> getOrgIds(@RequestHeader(RPCConstants.REQ_ORIGIN) String origin, @PathVariable("userId") String userId, @RequestParam("tenantId") String tenantId, @RequestParam("direction") String direction);
}
@@ -43,7 +43,7 @@ public class DictController {
* @param reqDict
* @return
*/
@Operation(summary = "字典-分页列表查询", description = "字典-分页列表查询")
@Operation(summary = "字典-分页列表查询", description = "字典-分页列表查询")
@GetMapping
@RequiresPermissions("sys:dict:query")
public Result<PageResult<Dict>> queryPageList(ReqDict reqDict, ReqPage reqPage) {
@@ -57,6 +57,7 @@ public class DictController {
* @param reqPage
* @return
*/
private List<Dict> queryList(ReqDict reqDict, ReqPage reqPage) {
PageHelper.startPage(reqPage.getPageNum(), reqPage.getPageSize());
LambdaQueryWrapper<Dict> queryWrapper = new LambdaQueryWrapper<Dict>()
@@ -74,7 +75,7 @@ public class DictController {
* @return
*/
@Log(title = "字典-添加", operateType = OperateType.INSERT)
@Operation(summary = "字典-添加", description = "字典-添加")
@Operation(summary = "字典-添加", description = "字典-添加")
@PostMapping
@RequiresPermissions("sys:dict:insert")
public Result<Dict> add(@RequestBody Dict dict) {
@@ -95,7 +96,7 @@ public class DictController {
* @return
*/
@Log(title = "字典-编辑", operateType = OperateType.UPDATE)
@Operation(summary = "字典-编辑", description = "字典-编辑")
@Operation(summary = "字典-编辑", description = "字典-编辑")
@PutMapping
@RequiresPermissions("sys:dict:update")
public Result<Dict> edit(@RequestBody Dict dict) {
@@ -132,7 +133,7 @@ public class DictController {
* @return
*/
@Log(title = "字典-通过id删除", operateType = OperateType.DELETE)
@Operation(summary = "字典-通过id删除", description = "字典-通过id删除")
@Operation(summary = "字典-通过id删除", description = "字典-通过id删除")
@DeleteMapping("/{id}")
@RequiresPermissions("sys:dict:delete")
public Result<Boolean> delete(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
@@ -145,7 +146,7 @@ public class DictController {
* @param id
* @return
*/
@Operation(summary = "字典-通过id查询", description = "字典-通过id查询")
@Operation(summary = "字典-通过id查询", description = "字典-通过id查询")
@GetMapping("/{id}")
@RequiresPermissions("sys:dict:query")
public Result<Dict> queryById(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
@@ -155,11 +156,12 @@ public class DictController {
/**
* 导出
*
* @param reqDict
* @param reqPage
* @throws IOException
*/
@Operation(summary = "导出字典", description = "导出字典")
@Operation(summary = "导出字典", description = "导出字典")
@GetMapping("/export")
@RequiresPermissions("sys:dict:query")
public void export(ReqDict reqDict, ReqPage reqPage) throws IOException {
@@ -14,17 +14,16 @@ import cn.com.mfish.common.dblink.entity.DataSourceOptions;
import cn.com.mfish.common.dblink.page.BoundSql;
import cn.com.mfish.common.dblink.page.MfPageHelper;
import cn.com.mfish.common.dblink.query.QueryHandler;
import cn.com.mfish.common.dblink.service.DbConnectService;
import cn.com.mfish.common.dblink.service.TableService;
import cn.com.mfish.sys.api.entity.DbConnect;
import cn.com.mfish.sys.api.entity.FieldInfo;
import cn.com.mfish.sys.api.entity.TableInfo;
import cn.com.mfish.common.dblink.service.DbConnectService;
import cn.com.mfish.common.dblink.service.TableService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
@@ -41,4 +41,9 @@ public class BootOrgService implements RemoteOrgService {
, new ReqOrgUser().setAccount(account).setNickname(nickname).setPhone(phone), reqPage)
, "组织下用户查询成功");
}
@Override
public Result<List<String>> getOrgIdsByFixCode(String origin, String tenantId, String codes, String direction) {
return ssoOrgService.getOrgIdsByFixCode(tenantId, List.of(codes.split(",")), TreeDirection.getDirection(direction));
}
}
@@ -0,0 +1,25 @@
package cn.com.mfish.common.api;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.api.remote.RemoteRoleService;
import cn.com.mfish.common.oauth.service.SsoRoleService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @description: 角色远程接口的本地实现
* @author: mfish
* @date: 2024/4/29
*/
@Service("remoteRoleService")
public class BootRoleService implements RemoteRoleService {
@Resource
SsoRoleService ssoRoleService;
@Override
public Result<List<String>> getRoleIdsByCode(String origin, String tenantId, String codes) {
return ssoRoleService.getRoleIdsByCode(tenantId, List.of(codes.split(",")));
}
}
@@ -65,4 +65,9 @@ public class BootUserService implements RemoteUserService {
public Result<List<SsoOrg>> getOrgs(String origin, String userId, String direction) {
return ssoUserService.getOrgs(userId, direction);
}
@Override
public Result<List<String>> getOrgIds(String origin, String userId, String tenantId, String direction) {
return ssoUserService.getOrgIds(tenantId, userId, direction);
}
}
@@ -1,4 +1,4 @@
package cn.com.mfish.common.ds.scope;
package cn.com.mfish.common.core.scope;
/**
* @description: 数据范围处理
@@ -6,6 +6,5 @@ package cn.com.mfish.common.ds.scope;
* @date: 2024/4/26
*/
public interface DataScopeHandle {
String sqlChange(String sql, String table, String fieldName, String value);
String buildCondition(String fieldName, String[] values);
}
@@ -76,11 +76,7 @@ public class AuthInfoUtils {
* @return
*/
public static String getCurrentUserId() {
String userId = getAttr(RPCConstants.REQ_USER_ID);
// if (StringUtils.isEmpty(userId)) {
// throw new OAuthValidateException("错误:未获取到当前用户id");
// }
return userId;
return getAttr(RPCConstants.REQ_USER_ID);
}
/**
@@ -1,5 +1,6 @@
package cn.com.mfish.common.core.utils;
import cn.com.mfish.common.core.constants.ServiceConstants;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@@ -64,16 +65,20 @@ public final class SpringBeanFactory implements BeanFactoryPostProcessor {
return beanFactory.getType(name);
}
// /**
// * 获取FeignClient的bean
// *
// * @param name
// * @param tClass
// * @param <T>
// * @return
// */
// public static <T> T getFeignBean(String name, Class<T> tClass) {
// FeignContext feignContext = beanFactory.getBean(FeignContext.class);
// return feignContext.getInstance(name, tClass);
// }
/**
* 获取远程服务
* @param clazz 远程服务类型
* @return 远程服务bean
* @param <T>
*/
public static <T> T getRemoteService(Class<T> clazz) {
T remoteService;
//单体服务通过name获取RPC的覆盖服务
if (ServiceConstants.isBoot(Utils.getServiceType())) {
remoteService = SpringBeanFactory.getBean(StringUtils.firstLowerCase(clazz.getSimpleName()));
} else {
remoteService = SpringBeanFactory.getBean(clazz);
}
return remoteService;
}
}
@@ -1,25 +0,0 @@
package cn.com.mfish.common.ds.common;
import cn.com.mfish.common.ds.scope.DataScopeHandle;
import cn.com.mfish.common.ds.scope.RoleDataScopeHandle;
import cn.com.mfish.common.ds.scope.TenantDataScopeHandle;
import lombok.Getter;
/**
* @description: 数据范围
* @author: mfish
* @date: 2024/4/26
*/
@Getter
public enum DataScopeType {
Tenant(0, new TenantDataScopeHandle()),
Role(1, new RoleDataScopeHandle());
private final int value;
private final DataScopeHandle handle;
DataScopeType(int value, DataScopeHandle handle) {
this.value = value;
this.handle = handle;
}
}
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import org.apache.ibatis.session.Configuration;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -16,8 +17,8 @@ import java.util.List;
@Component
public class BatchSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
// 注入InsertBatchSomeColumn
// 在!t.isLogicDelete()表示不要逻辑删除字段
methodList.add(new InsertBatchSomeColumn(t -> !t.isLogicDelete()));
@@ -1,15 +0,0 @@
package cn.com.mfish.common.ds.scope;
/**
* @description: 租户数据处理
* @author: mfish
* @date: 2024/4/26
*/
public class RoleDataScopeHandle implements DataScopeHandle {
@Override
public String sqlChange(String sql, String table, String fieldName,String value) {
//todo 补充逻辑
return sql;
}
}
@@ -1,33 +0,0 @@
package cn.com.mfish.common.ds.scope;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.ds.common.DataScopeUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* @description: 租户数据处理
* @author: mfish
* @date: 2024/4/26
*/
public class TenantDataScopeHandle implements DataScopeHandle {
@Override
public String sqlChange(String sql, String table, String fieldName,String value) {
fieldName = StringUtils.isEmpty(fieldName) ? "tenant_id" : fieldName;
value = StringUtils.isEmpty(value) ? AuthInfoUtils.getCurrentTenantId() : value;
List<DataScopeUtils.SqlSplit> list = DataScopeUtils.splitSql(sql, table);
for (DataScopeUtils.SqlSplit split : list) {
if (split.isHaveTable()) {
split.setStatement(split.getStatement().replaceAll(table
, "(select * from " + table + " where " + fieldName + "='" + value + "')"));
if (split.isHaveAlias()) {
continue;
}
split.setStatement(split.getStatement() + " " + table);
}
}
return list.stream().map(DataScopeUtils.SqlSplit::getStatement).collect(Collectors.joining());
}
}
@@ -1,5 +1,3 @@
cn.com.mfish.common.ds.config.MybatisInterceptor
cn.com.mfish.common.ds.config.SelectInterceptor
cn.com.mfish.common.ds.aspect.DataScopeAspect
cn.com.mfish.common.ds.config.BatchSqlInjector
com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure
@@ -1,6 +1,6 @@
package cn.com.mfish.common.ds.annotation;
package cn.com.mfish.common.oauth.annotation;
import cn.com.mfish.common.ds.common.DataScopeType;
import cn.com.mfish.common.oauth.common.DataScopeType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -18,24 +18,28 @@ public @interface DataScope {
/**
* 需要进行权限控制的表
*
* @return
* @return 表名
*/
String table() default "";
/**
* 进行权限控制的类型
*
* @return
* @return 权限控制类型
*/
DataScopeType type() default DataScopeType.Tenant;
/**
* 表进行过滤的字段名称
* 注意:fieldNames与tables数组值一一对应
* 表进行过滤的字段名称(不填 采用默认值。例如:租户使用tenant_id)
*
* @return
* @return 字段名
*/
String fieldName() default "";
String value() default "";
/**
* 字段值(不填各权限控制使用默认值,传入优先使用传入值。例如:租户使用当前租户)
*
* @return 字段值
*/
String[] values() default {};
}
@@ -1,4 +1,4 @@
package cn.com.mfish.common.ds.annotation;
package cn.com.mfish.common.oauth.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -1,9 +1,9 @@
package cn.com.mfish.common.ds.aspect;
package cn.com.mfish.common.oauth.aspect;
import cn.com.mfish.common.core.annotation.GlobalException;
import cn.com.mfish.common.ds.annotation.DataScope;
import cn.com.mfish.common.ds.annotation.DataScopes;
import cn.com.mfish.common.ds.common.DataScopeUtils;
import cn.com.mfish.common.oauth.annotation.DataScope;
import cn.com.mfish.common.oauth.annotation.DataScopes;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
@@ -29,7 +29,7 @@ import java.util.List;
@Slf4j
public class DataScopeAspect {
@Before("@annotation(cn.com.mfish.common.ds.annotation.DataScopes)||@annotation(cn.com.mfish.common.ds.annotation.DataScope)")
@Before("@annotation(cn.com.mfish.common.oauth.annotation.DataScopes)||@annotation(cn.com.mfish.common.oauth.annotation.DataScope)")
public void doBefore(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
@@ -43,14 +43,14 @@ public class DataScopeAspect {
DataScopeUtils.context.set(list);
}
@AfterReturning("@annotation(cn.com.mfish.common.ds.annotation.DataScopes)||@annotation(cn.com.mfish.common.ds.annotation.DataScope)")
@AfterReturning("@annotation(cn.com.mfish.common.oauth.annotation.DataScopes)||@annotation(cn.com.mfish.common.oauth.annotation.DataScope)")
public void doAfterReturning() {
DataScopeUtils.context.remove();
}
@AfterThrowing("@annotation(cn.com.mfish.common.ds.annotation.DataScopes)||@annotation(cn.com.mfish.common.ds.annotation.DataScope)")
public void doAfterThrowing() {
@AfterThrowing(value = "@annotation(cn.com.mfish.common.oauth.annotation.DataScopes)||@annotation(cn.com.mfish.common.oauth.annotation.DataScope)", throwing = "e")
public void doAfterThrowing(Throwable e) {
log.error("doAfterThrowing", e);
DataScopeUtils.context.remove();
}
}
@@ -0,0 +1,34 @@
package cn.com.mfish.common.oauth.common;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import cn.com.mfish.common.oauth.scope.OrgDataScopeHandle;
import cn.com.mfish.common.oauth.scope.RoleDataScopeHandle;
import cn.com.mfish.common.oauth.scope.TenantDataScopeHandle;
import cn.com.mfish.common.oauth.scope.UserDataScopeHandle;
import lombok.Getter;
/**
* @description: 数据范围
* @author: mfish
* @date: 2024/4/26
*/
@Getter
public enum DataScopeType {
//租户
Tenant(0, new TenantDataScopeHandle()),
//用户
User(1, new UserDataScopeHandle()),
//角色
Role(2, new RoleDataScopeHandle()),
//组织
Org(3, new OrgDataScopeHandle());
private final int value;
private final DataScopeHandle handle;
DataScopeType(int value, DataScopeHandle handle) {
this.value = value;
this.handle = handle;
}
}
@@ -1,6 +1,8 @@
package cn.com.mfish.common.ds.common;
package cn.com.mfish.common.oauth.common;
import cn.com.mfish.common.ds.annotation.DataScope;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.oauth.annotation.DataScope;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -22,6 +24,17 @@ public class DataScopeUtils {
public static Set<Character> START_KEYWORD = new HashSet<>();
public static Set<Character> END_KEYWORD = new HashSet<>();
@Data
@Accessors(chain = true)
public static class SqlSplit {
@Schema(name = "语句类型")
private boolean haveTable = false;
@Schema(name = "语句")
private String statement;
@Schema(name = "是否存在别名")
private boolean haveAlias = false;
}
static {
START_KEYWORD.add('.');
START_KEYWORD.add('(');
@@ -135,14 +148,31 @@ public class DataScopeUtils {
return sb.toString();
}
@Data
@Accessors(chain = true)
public static class SqlSplit {
@Schema(name = "语句类型")
private boolean haveTable = false;
@Schema(name = "语句")
private String statement;
@Schema(name = "是否存在别名")
private boolean haveAlias = false;
/**
* 构建查询条件
*
* @param fieldName
* @param values
* @return
*/
public static String buildCondition(String fieldName, String[] values) {
if (StringUtils.isEmpty(fieldName)) {
throw new MyRuntimeException("错误:未传入条件字段名称");
}
if (values == null || values.length == 0) {
throw new MyRuntimeException("错误:未传入条件值");
}
StringBuilder sb = new StringBuilder();
sb.append(fieldName);
if (values.length > 1) {
sb.append(" in ('");
sb.append(String.join("','", values));
sb.append("')");
} else {
sb.append(" = '");
sb.append(values[0]);
sb.append("'");
}
return sb.toString();
}
}
@@ -0,0 +1,22 @@
package cn.com.mfish.common.oauth.common;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @description: 数据范围值
* @author: mfish
* @date: 2024/4/29
*/
@Data
@Accessors(chain = true)
public class DataScopeValue {
@Schema(name = "数据处理方式")
private DataScopeHandle dataScopeHandle;
@Schema(name = "字段名称")
private String fieldName;
@Schema(name = "字段值")
private String[] values;
}
@@ -1,12 +1,10 @@
package cn.com.mfish.common.oauth.common;
import cn.com.mfish.common.core.constants.RPCConstants;
import cn.com.mfish.common.core.constants.ServiceConstants;
import cn.com.mfish.common.core.enums.DeviceType;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.SpringBeanFactory;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.core.utils.Utils;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.annotation.RequiresPermissions;
import cn.com.mfish.common.oauth.annotation.RequiresRoles;
@@ -35,18 +33,15 @@ import java.util.stream.Collectors;
public class OauthUtils {
private OauthUtils() {
}
/**
* 获取远程用户接口服务
*
* @return
*/
private static RemoteUserService getRemoteUserService() {
RemoteUserService remoteUserService;
//单体服务通过name获取RPC的覆盖服务
if (ServiceConstants.isBoot(Utils.getServiceType())) {
remoteUserService = SpringBeanFactory.getBean(StringUtils.firstLowerCase(RemoteUserService.class.getSimpleName()));
} else {
remoteUserService = SpringBeanFactory.getBean(RemoteUserService.class);
}
return remoteUserService;
return SpringBeanFactory.getRemoteService(RemoteUserService.class);
}
/**
@@ -1,4 +1,4 @@
package cn.com.mfish.oauth.entity;
package cn.com.mfish.common.oauth.entity;
import cn.com.mfish.common.core.entity.BaseEntity;
import com.baomidou.mybatisplus.annotation.IdType;
@@ -0,0 +1,78 @@
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import cn.com.mfish.common.oauth.common.DataScopeValue;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @description: 混合数据范围控制
* @author: mfish
* @date: 2024/4/29
*/
public class MixDataScopeHandle {
/**
* 转换为数据权限SQL
*
* @param oriSql
* @param map
* @return
*/
public static String sqlChange(String oriSql, Map<String, List<DataScopeValue>> map) {
for (Map.Entry<String, List<DataScopeValue>> entry : map.entrySet()) {
if (!oriSql.toLowerCase().contains(entry.getKey())) {
continue;
}
StringBuilder sb = new StringBuilder();
for (DataScopeValue value : entry.getValue()) {
sb.append(value.getDataScopeHandle().buildCondition(value.getFieldName(), value.getValues()));
sb.append(" and ");
}
if (!sb.isEmpty()) {
sb.delete(sb.length() - 5, sb.length());
}
oriSql = sqlChange(oriSql, entry.getKey(), sb.toString());
}
return oriSql;
}
/**
* 转换为数据权限SQL
*
* @param sql
* @param table
* @param condition
* @return
*/
private static String sqlChange(String sql, String table, String condition) {
List<DataScopeUtils.SqlSplit> list = DataScopeUtils.splitSql(sql, table);
for (DataScopeUtils.SqlSplit split : list) {
if (split.isHaveTable()) {
split.setStatement(split.getStatement().replaceAll(table, buildReplaceSql(table, condition)));
if (split.isHaveAlias()) {
continue;
}
split.setStatement(split.getStatement() + " " + table);
}
}
return list.stream().map(DataScopeUtils.SqlSplit::getStatement).collect(Collectors.joining());
}
/**
* 构建替换SQL
*
* @param table
* @param condition
* @return
*/
private static String buildReplaceSql(String table, String condition) {
return "(select * from " + table +
" where " +
condition +
")";
}
}
@@ -0,0 +1,71 @@
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.core.constants.RPCConstants;
import cn.com.mfish.common.core.enums.TreeDirection;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.SpringBeanFactory;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.api.remote.RemoteOrgService;
import cn.com.mfish.common.oauth.api.remote.RemoteUserService;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* @description: 组织数据权限控制
* @author: mfish
* @date: 2024/4/29
*/
public class OrgDataScopeHandle implements DataScopeHandle {
private static final String DEFAULT_FIELD = "org_id";
@Override
public String buildCondition(String fieldName, String[] values) {
fieldName = StringUtils.isEmpty(fieldName) ? DEFAULT_FIELD : fieldName;
if (values == null || values.length == 0) {
//未传值时使用当前用户的所有角色id
values = getCurOrgs();
} else {
values = getOrgIdsByCode(values);
}
return DataScopeUtils.buildCondition(fieldName, values);
}
private String[] getCurOrgs() {
final String userId = AuthInfoUtils.getCurrentUserId();
final String tenantId = AuthInfoUtils.getCurrentTenantId();
RemoteUserService remoteUserService = SpringBeanFactory.getRemoteService(RemoteUserService.class);
//异步调用防止切面主线程使用了PageHelper分页交叉
CompletableFuture<Result<List<String>>> future = CompletableFuture.supplyAsync(() -> remoteUserService.getOrgIds(RPCConstants.INNER, userId, tenantId, TreeDirection.向下.toString()));
try {
Result<List<String>> result = future.get();
if (!result.isSuccess()) {
throw new MyRuntimeException(result.getMsg());
}
return result.getData().toArray(new String[0]);
} catch (InterruptedException | ExecutionException e) {
throw new MyRuntimeException(e.getMessage());
}
}
private String[] getOrgIdsByCode(final String[] values) {
final String tenantId = AuthInfoUtils.getCurrentTenantId();
RemoteOrgService remoteOrgService = SpringBeanFactory.getRemoteService(RemoteOrgService.class);
//异步调用防止切面主线程使用了PageHelper分页交叉
CompletableFuture<Result<List<String>>> future = CompletableFuture.supplyAsync(() -> remoteOrgService.getOrgIdsByFixCode(RPCConstants.INNER, tenantId, String.join(",", values), TreeDirection.向下.toString()));
try {
Result<List<String>> result = future.get();
if (!result.isSuccess()) {
throw new MyRuntimeException(result.getMsg());
}
return result.getData().toArray(new String[0]);
} catch (InterruptedException | ExecutionException e) {
throw new MyRuntimeException(e.getMessage());
}
}
}
@@ -0,0 +1,71 @@
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.core.constants.RPCConstants;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.SpringBeanFactory;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.api.entity.UserRole;
import cn.com.mfish.common.oauth.api.remote.RemoteRoleService;
import cn.com.mfish.common.oauth.api.remote.RemoteUserService;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* @description: 租户数据处理
* @author: mfish
* @date: 2024/4/26
*/
public class RoleDataScopeHandle implements DataScopeHandle {
private static final String DEFAULT_FIELD = "role_id";
@Override
public String buildCondition(String fieldName, String[] values) {
fieldName = StringUtils.isEmpty(fieldName) ? DEFAULT_FIELD : fieldName;
if (values == null || values.length == 0) {
//未传值时使用当前用户的所有角色id
values = getCurRoles();
} else {
values = getRoleIdsByCode(values);
}
return DataScopeUtils.buildCondition(fieldName, values);
}
private String[] getCurRoles() {
final String userId = AuthInfoUtils.getCurrentUserId();
final String tenantId = AuthInfoUtils.getCurrentTenantId();
RemoteUserService remoteUserService = SpringBeanFactory.getRemoteService(RemoteUserService.class);
//异步调用防止切面主线程使用了PageHelper分页交叉
CompletableFuture<Result<List<UserRole>>> future = CompletableFuture.supplyAsync(() -> remoteUserService.getRoles(RPCConstants.INNER, userId, tenantId));
try {
Result<List<UserRole>> result = future.get();
if (!result.isSuccess()) {
throw new MyRuntimeException(result.getMsg());
}
return result.getData().stream().map(UserRole::getId).toList().toArray(String[]::new);
} catch (InterruptedException | ExecutionException e) {
throw new MyRuntimeException(e.getMessage());
}
}
private String[] getRoleIdsByCode(final String[] values) {
final String tenantId = AuthInfoUtils.getCurrentTenantId();
RemoteRoleService remoteRoleService = SpringBeanFactory.getRemoteService(RemoteRoleService.class);
//异步调用防止切面主线程使用了PageHelper分页交叉
CompletableFuture<Result<List<String>>> future = CompletableFuture.supplyAsync(() -> remoteRoleService.getRoleIdsByCode(RPCConstants.INNER, tenantId, String.join(",", values)));
try {
Result<List<String>> result = future.get();
if (!result.isSuccess()) {
throw new MyRuntimeException(result.getMsg());
}
return result.getData().toArray(new String[0]);
} catch (InterruptedException | ExecutionException e) {
throw new MyRuntimeException(e.getMessage());
}
}
}
@@ -1,8 +1,9 @@
package cn.com.mfish.common.ds.config;
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.ds.annotation.DataScope;
import cn.com.mfish.common.ds.common.DataScopeUtils;
import cn.com.mfish.common.oauth.annotation.DataScope;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import cn.com.mfish.common.oauth.common.DataScopeValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
@@ -19,7 +20,10 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @description: 查询拦截器
@@ -51,13 +55,8 @@ public class SelectInterceptor implements Interceptor {
BoundSql boundSql = statementHandler.getBoundSql();
String oriSql = boundSql.getSql();
try {
for (DataScope scope : scopes) {
String tableName = scope.table();
if (!oriSql.toLowerCase().contains(tableName.toLowerCase())) {
continue;
}
oriSql = scope.type().getHandle().sqlChange(oriSql, tableName, scope.fieldName(), scope.value());
}
Map<String, List<DataScopeValue>> map = buildParamsMap(scopes);
oriSql = MixDataScopeHandle.sqlChange(oriSql, map);
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, oriSql);
@@ -67,4 +66,20 @@ public class SelectInterceptor implements Interceptor {
}
return invocation.proceed();
}
private Map<String, List<DataScopeValue>> buildParamsMap(List<DataScope> scopes) {
Map<String, List<DataScopeValue>> map = new HashMap<>();
for (DataScope scope : scopes) {
String tableName = scope.table();
List<DataScopeValue> list;
if (map.containsKey(tableName)) {
list = map.get(tableName);
} else {
list = new ArrayList<>();
}
list.add(new DataScopeValue().setDataScopeHandle(scope.type().getHandle()).setFieldName(scope.fieldName()).setValues(scope.values()));
map.put(tableName, list);
}
return map;
}
}
@@ -0,0 +1,25 @@
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
import cn.com.mfish.common.core.utils.StringUtils;
/**
* @description: 租户数据处理
* @author: mfish
* @date: 2024/4/26
*/
public class TenantDataScopeHandle implements DataScopeHandle {
private static final String DEFAULT_FIELD = "tenant_id";
@Override
public String buildCondition(String fieldName, String[] values) {
fieldName = StringUtils.isEmpty(fieldName) ? DEFAULT_FIELD : fieldName;
if (values == null || values.length == 0) {
//未传值时使用当前租户id
values = new String[]{AuthInfoUtils.getCurrentTenantId()};
}
return DataScopeUtils.buildCondition(fieldName, values);
}
}
@@ -0,0 +1,25 @@
package cn.com.mfish.common.oauth.scope;
import cn.com.mfish.common.core.scope.DataScopeHandle;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.oauth.common.DataScopeUtils;
/**
* @description: 用户数据权限限制
* @author: mfish
* @date: 2024/4/29
*/
public class UserDataScopeHandle implements DataScopeHandle {
private static final String DEFAULT_FIELD = "user_id";
@Override
public String buildCondition(String fieldName, String[] values) {
fieldName = StringUtils.isEmpty(fieldName) ? DEFAULT_FIELD : fieldName;
if (values == null || values.length == 0) {
//未传值时使用当前用户id
values = new String[]{AuthInfoUtils.getCurrentUserId()};
}
return DataScopeUtils.buildCondition(fieldName, values);
}
}
@@ -36,6 +36,8 @@ public interface SsoOrgService extends IService<SsoOrg> {
List<SsoOrg> queryOrgById(String id, TreeDirection direction);
Result<List<String>> queryOrgIdsById(String tenantId, List<String> ids, TreeDirection direction);
List<UserRole> getOrgRoles(String... orgIds);
Result<List<SsoOrg>> queryByIds(String ids);
@@ -43,4 +45,6 @@ public interface SsoOrgService extends IService<SsoOrg> {
boolean isTenantOrg(String orgId, String tenantId);
PageResult<UserInfo> queryUserByCode(String code, ReqOrgUser reqOrgUser, ReqPage reqPage);
Result<List<String>> getOrgIdsByFixCode(String tenantId, List<String> orgCodes, TreeDirection direction);
}
@@ -1,7 +1,7 @@
package cn.com.mfish.oauth.service;
package cn.com.mfish.common.oauth.service;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.oauth.entity.SsoRole;
import cn.com.mfish.common.oauth.entity.SsoRole;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -24,4 +24,6 @@ public interface SsoRoleService extends IService<SsoRole> {
List<String> getRoleMenus(String roleId);
boolean isTenantRole(String roleId, String tenantId);
Result<List<String>> getRoleIdsByCode(String tenantId, List<String> roleCodes);
}
@@ -53,6 +53,8 @@ public interface SsoUserService extends IService<SsoUser> {
Result<List<SsoOrg>> getOrgs(String userId, String direction);
Result<List<String>> getOrgIds(String tenantId, String userId, String direction);
/**
* 判断帐号是否存在
*
@@ -84,7 +86,7 @@ public interface SsoUserService extends IService<SsoUser> {
boolean isExistUserOrg(String userId, String orgId);
List<SimpleUserInfo> searchUserList(String condition);
List<SimpleUserInfo> searchUserList(String condition);
UserInfo getUserInfo(String userId);
@@ -3,4 +3,6 @@ cn.com.mfish.common.oauth.service.impl.WeChatTokenServiceImpl
cn.com.mfish.common.oauth.aspect.AuthorizationAspect
cn.com.mfish.common.oauth.validator.WebTokenValidator
cn.com.mfish.common.oauth.validator.WeChatTokenValidator
cn.com.mfish.common.oauth.validator.TokenValidator
cn.com.mfish.common.oauth.validator.TokenValidator
cn.com.mfish.common.oauth.scope.SelectInterceptor
cn.com.mfish.common.oauth.aspect.DataScopeAspect
@@ -49,7 +49,7 @@ public class SsoOrgController {
* @param reqPage
* @return
*/
@Operation(summary = "组织结构表-分页列表查询", description = "组织结构表-分页列表查询")
@Operation(summary = "组织结构表-分页列表查询", description = "组织结构表-分页列表查询")
@GetMapping
@RequiresPermissions("sys:org:query")
public Result<PageResult<SsoOrg>> queryPageList(ReqSsoOrg reqSsoOrg, ReqPage reqPage) {
@@ -90,7 +90,7 @@ public class SsoOrgController {
* @return
*/
@Log(title = "组织结构表-添加", operateType = OperateType.INSERT)
@Operation(summary = "组织结构表-添加", description = "组织结构表-添加")
@Operation(summary = "组织结构表-添加", description = "组织结构表-添加")
@PostMapping
@RequiresPermissions("sys:org:insert")
public Result<SsoOrg> add(@RequestBody SsoOrg ssoOrg) {
@@ -104,7 +104,7 @@ public class SsoOrgController {
* @return
*/
@Log(title = "组织结构表-编辑", operateType = OperateType.UPDATE)
@Operation(summary = "组织结构表-编辑", description = "组织结构表-编辑")
@Operation(summary = "组织结构表-编辑", description = "组织结构表-编辑")
@PutMapping
@RequiresPermissions("sys:org:update")
public Result<SsoOrg> edit(@RequestBody SsoOrg ssoOrg) {
@@ -118,7 +118,7 @@ public class SsoOrgController {
* @return
*/
@Log(title = "组织结构表-通过id删除", operateType = OperateType.DELETE)
@Operation(summary = "组织结构表-通过id删除", description = "组织结构表-通过id删除")
@Operation(summary = "组织结构表-通过id删除", description = "组织结构表-通过id删除")
@DeleteMapping("/{id}")
@RequiresPermissions("sys:org:delete")
public Result<Boolean> delete(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
@@ -131,14 +131,14 @@ public class SsoOrgController {
* @param ids 多个ID逗号分隔
* @return
*/
@Operation(summary = "组织结构表-通过id查询", description = "组织结构表-通过id查询")
@Operation(summary = "组织结构表-通过id查询", description = "组织结构表-通过id查询")
@GetMapping("/{ids}")
@RequiresPermissions("sys:org:query")
public Result<List<SsoOrg>> queryByIds(@Parameter(name = "ids", description = "唯一性ID") @PathVariable("ids") String ids) {
return ssoOrgService.queryByIds(ids);
}
@Operation(summary = "组织树-通过固定编码查询", description = "组织树-通过固定编码查询")
@Operation(summary = "组织树-通过固定编码查询", description = "组织树-通过固定编码查询")
@GetMapping("/code/{code}")
@Parameters({
@Parameter(name = "direction", description = "方向 all 返回所有父子节点 up返回父节点 down返回子节点", required = true),
@@ -148,9 +148,26 @@ public class SsoOrgController {
return Result.ok(list, "组织结构表-查询成功!");
}
@Operation(summary = "获取组织及子组织下的所有用户-通过固定编码查询", description = "获取组织及子组织下的所有用户-通过固定编码查询")
@Operation(summary = "获取组织及子组织下的所有用户-通过固定编码查询", description = "获取组织及子组织下的所有用户-通过固定编码查询")
@GetMapping("/user/{code}")
public Result<PageResult<UserInfo>> queryUserByCode(@Parameter(name = "code", description = "固定编码") @PathVariable("code") String code, ReqOrgUser reqOrgUser, ReqPage reqPage) {
return Result.ok(ssoOrgService.queryUserByCode(code, reqOrgUser, reqPage), "组织下用户查询成功");
}
/**
* 获取组织id-通过组织编码查询
*
* @param codes 组织编码,多个逗号分隔
* @return
*/
@Operation(summary = "获取组织id", description = "获取组织id-通过组织编码查询")
@GetMapping("/ids")
@Parameters({
@Parameter(name = "tenantId", description = "租户ID"),
@Parameter(name = "codes", description = "组织固定编码", required = true),
@Parameter(name = "direction", description = "方向 all 返回所有父子节点 up返回父节点 down返回子节点", required = true)
})
public Result<List<String>> getOrgIdsByFixCode(String tenantId, String codes, String direction) {
return ssoOrgService.getOrgIdsByFixCode(tenantId, List.of(codes.split(",")), TreeDirection.getDirection(direction));
}
}
@@ -1,5 +1,6 @@
package cn.com.mfish.oauth.controller;
import cn.com.mfish.common.core.annotation.InnerUser;
import cn.com.mfish.common.core.enums.OperateType;
import cn.com.mfish.common.core.utils.AuthInfoUtils;
import cn.com.mfish.common.core.utils.StringUtils;
@@ -9,14 +10,15 @@ import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.log.annotation.Log;
import cn.com.mfish.common.oauth.annotation.RequiresPermissions;
import cn.com.mfish.common.oauth.api.entity.SsoTenant;
import cn.com.mfish.oauth.entity.SsoRole;
import cn.com.mfish.common.oauth.entity.SsoRole;
import cn.com.mfish.oauth.mapper.SsoTenantMapper;
import cn.com.mfish.oauth.req.ReqSsoRole;
import cn.com.mfish.oauth.service.SsoRoleService;
import cn.com.mfish.common.oauth.service.SsoRoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -49,7 +51,7 @@ public class SsoRoleController {
* @param reqPage
* @return
*/
@Operation(summary = "角色信息表-分页列表查询", description = "角色信息表-分页列表查询")
@Operation(summary = "角色信息表-分页列表查询", description = "角色信息表-分页列表查询")
@GetMapping
@RequiresPermissions("sys:role:query")
public Result<PageResult<SsoRole>> queryPageList(ReqSsoRole reqSsoRole, ReqPage reqPage) {
@@ -65,7 +67,7 @@ public class SsoRoleController {
return Result.ok(ssoRoleService.getRoleMenus(roleId), "查询角色下菜单成功");
}
@Operation(summary = "角色信息表-列表查询", description = "角色信息表-列表查询")
@Operation(summary = "角色信息表-列表查询", description = "角色信息表-列表查询")
@GetMapping("/all")
public Result<List<SsoRole>> queryList(ReqSsoRole reqSsoRole) {
//组织参数不为空,获取组织所属租户的角色
@@ -86,11 +88,11 @@ public class SsoRoleController {
.like(reqSsoRole.getRoleCode() != null, SsoRole::getRoleCode, reqSsoRole.getRoleCode())
.like(reqSsoRole.getRoleName() != null, SsoRole::getRoleName, reqSsoRole.getRoleName())
.orderByAsc(SsoRole::getRoleSort);
if(!StringUtils.isEmpty(reqSsoRole.getTenantId())){
if (!StringUtils.isEmpty(reqSsoRole.getTenantId())) {
String[] ids = reqSsoRole.getTenantId().split(",");
wrapper.and(ssoRoleLambdaQueryWrapper -> {
for(String id : ids){
ssoRoleLambdaQueryWrapper.or().eq(!StringUtils.isEmpty(id),SsoRole::getTenantId,id);
for (String id : ids) {
ssoRoleLambdaQueryWrapper.or().eq(!StringUtils.isEmpty(id), SsoRole::getTenantId, id);
}
});
}
@@ -104,7 +106,7 @@ public class SsoRoleController {
* @return
*/
@Log(title = "角色信息表-添加", operateType = OperateType.INSERT)
@Operation(summary = "角色信息表-添加", description = "角色信息表-添加")
@Operation(summary = "角色信息表-添加", description = "角色信息表-添加")
@PostMapping
@RequiresPermissions("sys:role:insert")
public Result<SsoRole> add(@RequestBody SsoRole ssoRole) {
@@ -119,7 +121,7 @@ public class SsoRoleController {
* @return
*/
@Log(title = "角色信息表-编辑", operateType = OperateType.UPDATE)
@Operation(summary = "角色信息表-编辑", description = "角色信息表-编辑")
@Operation(summary = "角色信息表-编辑", description = "角色信息表-编辑")
@PutMapping
@RequiresPermissions("sys:role:update")
public Result<SsoRole> edit(@RequestBody SsoRole ssoRole) {
@@ -128,7 +130,7 @@ public class SsoRoleController {
}
@Log(title = "角色信息表-设置状态", operateType = OperateType.UPDATE)
@Operation(summary = "角色信息表-设置状态", description = "角色信息表-设置状态")
@Operation(summary = "角色信息表-设置状态", description = "角色信息表-设置状态")
@PutMapping("/status")
public Result<Boolean> setStatus(@RequestBody SsoRole ssoRole) {
if (ssoRoleService.updateById(new SsoRole().setId(ssoRole.getId()).setStatus(ssoRole.getStatus()))) {
@@ -144,7 +146,7 @@ public class SsoRoleController {
* @return
*/
@Log(title = "角色信息表-通过id删除", operateType = OperateType.DELETE)
@Operation(summary = "角色信息表-通过id删除", description = "角色信息表-通过id删除")
@Operation(summary = "角色信息表-通过id删除", description = "角色信息表-通过id删除")
@DeleteMapping("/{id}")
@RequiresPermissions("sys:role:delete")
public Result<Boolean> delete(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
@@ -160,11 +162,28 @@ public class SsoRoleController {
* @param id
* @return
*/
@Operation(summary = "角色信息表-通过id查询", description = "角色信息表-通过id查询")
@Operation(summary = "角色信息表-通过id查询", description = "角色信息表-通过id查询")
@GetMapping("/{id}")
@RequiresPermissions("sys:role:query")
public Result<SsoRole> queryById(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
SsoRole ssoRole = ssoRoleService.getById(id);
return Result.ok(ssoRole, "角色信息表-查询成功!");
}
/**
* 获取角色id-通过角色编码查询
*
* @param codes 角色编码,多个逗号分隔
* @return
*/
@Operation(summary = "获取角色id", description = "获取角色id-通过角色编码查询")
@GetMapping("/ids/{codes}")
@InnerUser
@Parameters({
@Parameter(name = "tenantId", description = "租户ID", required = true),
@Parameter(name = "codes", description = "角色编码", required = true)
})
public Result<List<String>> getRoleIdsByCode(String tenantId, String codes) {
return ssoRoleService.getRoleIdsByCode(tenantId, List.of(codes.split(",")));
}
}
@@ -9,8 +9,8 @@ import cn.com.mfish.common.core.utils.excel.ExcelUtils;
import cn.com.mfish.common.core.web.PageResult;
import cn.com.mfish.common.core.web.ReqPage;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.ds.annotation.DataScope;
import cn.com.mfish.common.ds.common.DataScopeType;
import cn.com.mfish.common.oauth.annotation.DataScope;
import cn.com.mfish.common.oauth.common.DataScopeType;
import cn.com.mfish.common.log.annotation.Log;
import cn.com.mfish.common.oauth.annotation.RequiresPermissions;
import cn.com.mfish.common.oauth.api.entity.SsoMenu;
@@ -28,11 +28,11 @@ import cn.com.mfish.common.oauth.service.SsoMenuService;
import cn.com.mfish.common.oauth.service.SsoOrgService;
import cn.com.mfish.common.oauth.service.SsoUserService;
import cn.com.mfish.oauth.cache.common.ClearCache;
import cn.com.mfish.oauth.entity.SsoRole;
import cn.com.mfish.common.oauth.entity.SsoRole;
import cn.com.mfish.oauth.entity.UserOrg;
import cn.com.mfish.oauth.req.ReqSsoRole;
import cn.com.mfish.oauth.req.ReqSsoTenant;
import cn.com.mfish.oauth.service.SsoRoleService;
import cn.com.mfish.common.oauth.service.SsoRoleService;
import cn.com.mfish.oauth.service.SsoTenantService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
@@ -92,7 +92,7 @@ public class SsoUserController {
*
* @return
*/
@Operation(summary = "获取当前用户租户列表", description = "获取当前用户租户列表")
@Operation(summary = "获取当前用户租户列表", description = "获取当前用户租户列表")
@GetMapping("/tenants")
public Result<List<TenantVo>> getUserTenants(String userId) {
if (StringUtils.isEmpty(userId)) {
@@ -101,9 +101,8 @@ public class SsoUserController {
return Result.ok(ssoUserService.getUserTenants(userId), "获取当前租户列表成功!");
}
@Operation(summary = "获取用户组织")
@Operation(summary = "获取用户组织树")
@GetMapping("/orgs/{userId}")
@Log(title = "获取用户组织", operateType = OperateType.QUERY)
@Parameters({
@Parameter(name = "direction", description = "方向 all 返回所有父子节点 up返回父节点 down返回子节点", required = true)
})
@@ -111,6 +110,16 @@ public class SsoUserController {
return ssoUserService.getOrgs(userId, direction);
}
@Operation(summary = "获取用户组织ID列表")
@GetMapping("/orgIds/{userId}")
@Parameters({
@Parameter(name = "tenantId", description = "租户id"),
@Parameter(name = "direction", description = "方向 all 返回所有父子节点 up返回父节点 down返回子节点", required = true)
})
public Result<List<String>> getOrgIds(@PathVariable("userId") String userId, @RequestParam String tenantId, @RequestParam String direction) {
return ssoUserService.getOrgIds(tenantId, userId, direction);
}
@Operation(summary = "通过用户ID获取用户")
@GetMapping("/{id}")
public Result<UserInfo> getUserById(@Parameter(name = "id", description = "用户ID") @PathVariable String id) {
@@ -133,7 +142,7 @@ public class SsoUserController {
}
@Log(title = "用户-设置状态", operateType = OperateType.UPDATE)
@Operation(summary = "用户-设置状态", description = "用户-设置状态")
@Operation(summary = "用户-设置状态", description = "用户-设置状态")
@PutMapping("/status")
@RequiresPermissions("sys:account:update")
public Result<Boolean> setStatus(@RequestBody SsoUser ssoUser) {
@@ -146,7 +155,7 @@ public class SsoUserController {
return Result.fail(false, "错误:用户-设置状态失败!");
}
@Operation(summary = "用户登出", description = "用户登出--该方法只适用于web前端登录的用户登出")
@Operation(summary = "用户登出", description = "用户登出--该方法只适用于web前端登录的用户登出")
@GetMapping("/revoke")
@Log(title = "用户登出", operateType = OperateType.LOGOUT)
public Result<Boolean> revoke() {
@@ -195,7 +204,7 @@ public class SsoUserController {
* @param reqPage
* @return
*/
@Operation(summary = "用户信息-分页列表查询", description = "用户信息-分页列表查询")
@Operation(summary = "用户信息-分页列表查询", description = "用户信息-分页列表查询")
@GetMapping
@RequiresPermissions("sys:account:query")
public Result<PageResult<UserInfo>> queryPageList(ReqSsoUser reqSsoUser, ReqPage reqPage) {
@@ -204,7 +213,7 @@ public class SsoUserController {
return Result.ok(new PageResult<>(pageList), "用户信息-查询成功!");
}
@Operation(summary = "检索用户列表-限制最多查询50人(有新增租户用户权限人允许检索)", description = "检索用户列表")
@Operation(summary = "检索用户列表-限制最多查询50人(有新增租户用户权限人允许检索)", description = "检索用户列表")
@GetMapping("/search")
@Parameters({
@Parameter(name = "condition", description = "检索条件,可输入用户名、昵称、手机号")
@@ -215,7 +224,7 @@ public class SsoUserController {
}
@Log(title = "用户信息-添加", operateType = OperateType.INSERT)
@Operation(summary = "用户信息-添加", description = "用户信息-添加")
@Operation(summary = "用户信息-添加", description = "用户信息-添加")
@PostMapping
@RequiresPermissions(value = {"sys:account:insert", "sys:tenantUser:insert"})
public Result<SsoUser> add(@RequestBody SsoUser ssoUser) {
@@ -229,7 +238,7 @@ public class SsoUserController {
* @return
*/
@Log(title = "用户信息-编辑", operateType = OperateType.UPDATE)
@Operation(summary = "用户信息-编辑", description = "用户信息-编辑")
@Operation(summary = "用户信息-编辑", description = "用户信息-编辑")
@PutMapping
@RequiresPermissions("sys:account:update")
public Result<SsoUser> edit(@RequestBody SsoUser ssoUser) {
@@ -237,7 +246,7 @@ public class SsoUserController {
}
@Log(title = "用户信息-编辑", operateType = OperateType.UPDATE)
@Operation(summary = "用户信息-编辑", description = "用户信息-编辑")
@Operation(summary = "用户信息-编辑", description = "用户信息-编辑")
@PutMapping("/me")
public Result<SsoUser> editMe(@RequestBody SsoUser ssoUser) {
if (!ssoUser.getId().equals(AuthInfoUtils.getCurrentUserId())) {
@@ -253,7 +262,7 @@ public class SsoUserController {
* @return
*/
@Log(title = "用户信息-通过id删除", operateType = OperateType.DELETE)
@Operation(summary = "用户信息-通过id删除", description = "用户信息-通过id删除")
@Operation(summary = "用户信息-通过id删除", description = "用户信息-通过id删除")
@DeleteMapping("/{id}")
@RequiresPermissions("sys:account:delete")
public Result<Boolean> delete(@Parameter(name = "id", description = "唯一性ID") @PathVariable String id) {
@@ -46,4 +46,6 @@ public interface SsoOrgMapper extends BaseMapper<SsoOrg> {
int isTenantOrg(@Param("orgId") String orgId, @Param("tenantId") String tenantId);
List<UserInfo> queryUserByCode(@Param("code") String code, @Param("reqOrgUser") ReqOrgUser reqOrgUser);
List<String> getOrgDownIdsByCode(@Param("orgCodes") List<String> orgCodes);
}
@@ -1,6 +1,6 @@
package cn.com.mfish.oauth.mapper;
import cn.com.mfish.oauth.entity.SsoRole;
import cn.com.mfish.common.oauth.entity.SsoRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
@@ -34,4 +34,6 @@ public interface SsoRoleMapper extends BaseMapper<SsoRole> {
List<String> getRoleMenus(String roleId);
int isTenantRole(@Param("roleId") String roleId, @Param("tenantId") String tenantId);
List<String> getRoleIdsByCode(@Param("tenantId") String tenantId, @Param("roleCodes") List<String> roleCodes);
}
@@ -142,4 +142,11 @@
and su.phone like CONCAT('%',#{reqOrgUser.phone},'%')
</if>
</select>
<select id="getOrgDownIdsByCode" resultType="java.lang.String">
select id from sso_org where 1!=1
<foreach collection="orgCodes" item="code">
or org_code like CONCAT(#{code},'%')
</foreach>
order by org_sort
</select>
</mapper>
@@ -16,7 +16,7 @@
</if>
</where>
</select>
<resultMap id="roleMap" type="cn.com.mfish.oauth.entity.SsoRole">
<resultMap id="roleMap" type="cn.com.mfish.common.oauth.entity.SsoRole">
<result property="menus" column="menus" typeHandler="cn.com.mfish.oauth.handler.StrToListTypeHandler"/>
</resultMap>
<select id="getRoleUser" resultType="string">
@@ -31,4 +31,10 @@
<select id="isTenantRole" resultType="java.lang.Integer">
select count(0) from sso_role where tenant_id = #{tenantId} and id = #{roleId}
</select>
<select id="getRoleIdsByCode" resultType="java.lang.String">
select id from sso_role where tenant_id=#{tenantId} and role_code in
<foreach collection="roleCodes" item="code" separator="," open="(" close=")">
#{code}
</foreach>
</select>
</mapper>
@@ -24,6 +24,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
@@ -190,6 +191,15 @@ public class SsoOrgServiceImpl extends ServiceImpl<SsoOrgMapper, SsoOrg> impleme
return queryOrg(org, direction);
}
@Override
public Result<List<String>> queryOrgIdsById(String tenantId, List<String> ids, TreeDirection direction) {
if (ids == null || ids.isEmpty()) {
throw new MyRuntimeException("错误:组织id不允许为空");
}
List<SsoOrg> list = baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getId, ids));
return getOrgIdsByOrg(tenantId, list, direction);
}
@Override
public List<UserRole> getOrgRoles(String... orgIds) {
return baseMapper.getOrgRoles(orgIds);
@@ -218,6 +228,36 @@ public class SsoOrgServiceImpl extends ServiceImpl<SsoOrgMapper, SsoOrg> impleme
return new PageResult<>(baseMapper.queryUserByCode(code, reqOrgUser));
}
@Override
public Result<List<String>> getOrgIdsByFixCode(String tenantId, List<String> orgCodes, TreeDirection direction) {
List<SsoOrg> list = baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getOrgFixCode, orgCodes));
return getOrgIdsByOrg(tenantId, list, direction);
}
private Result<List<String>> getOrgIdsByOrg(String tenantId, List<SsoOrg> list, TreeDirection direction) {
//todo tenantId暂时未用到,后续完善
switch (direction) {
case 向下:
return Result.ok(baseMapper.getOrgDownIdsByCode(list.stream().map(SsoOrg::getOrgCode).toList()), "查询下级组织ID成功");
case 向上:
List<String> listCode = new ArrayList<>();
for (SsoOrg org : list) {
listCode.addAll(getUpOrgCodes(org.getOrgCode(), org.getOrgLevel()));
}
return Result.ok(baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getOrgCode, listCode)).stream().map(SsoOrg::getId).toList(), "查询上级组织ID成功");
default:
List<String> ids = baseMapper.getOrgDownIdsByCode(list.stream().map(SsoOrg::getOrgCode).toList());
List<String> codes = new ArrayList<>();
for (SsoOrg org : list) {
codes.addAll(getUpOrgCodes(org.getOrgCode(), org.getOrgLevel() - 1));
}
if (!codes.isEmpty()) {
ids.addAll(baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getOrgCode, codes)).stream().map(SsoOrg::getId).toList());
}
return Result.ok(ids, "查询所有组织ID成功");
}
}
List<SsoOrg> queryOrg(SsoOrg org, TreeDirection direction) {
if (org == null) {
return new ArrayList<>();
@@ -262,6 +302,21 @@ public class SsoOrgServiceImpl extends ServiceImpl<SsoOrgMapper, SsoOrg> impleme
* @return
*/
private List<SsoOrg> upOrg(String code, int level) {
List<String> orgList = getUpOrgCodes(code, level);
if (orgList.isEmpty()) {
return new ArrayList<>();
}
return baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getOrgCode, orgList));
}
/**
* 获取所有上级编码
*
* @param code
* @param level
* @return
*/
private List<String> getUpOrgCodes(String code, int level) {
List<String> orgList = new ArrayList<>();
if (level < 1) {
return new ArrayList<>();
@@ -269,6 +324,7 @@ public class SsoOrgServiceImpl extends ServiceImpl<SsoOrgMapper, SsoOrg> impleme
for (int i = 1; i <= level; i++) {
orgList.add(code.substring(0, i * 5));
}
return baseMapper.selectList(new LambdaQueryWrapper<SsoOrg>().in(SsoOrg::getOrgCode, orgList));
return orgList;
}
}
@@ -3,16 +3,16 @@ package cn.com.mfish.oauth.service.impl;
import cn.com.mfish.common.core.exception.MyRuntimeException;
import cn.com.mfish.common.core.utils.StringUtils;
import cn.com.mfish.common.core.web.Result;
import cn.com.mfish.common.oauth.entity.SsoRole;
import cn.com.mfish.common.oauth.service.SsoRoleService;
import cn.com.mfish.oauth.cache.common.ClearCache;
import cn.com.mfish.oauth.entity.SsoRole;
import cn.com.mfish.oauth.mapper.SsoRoleMapper;
import cn.com.mfish.oauth.service.SsoRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.text.MessageFormat;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -123,4 +123,9 @@ public class SsoRoleServiceImpl extends ServiceImpl<SsoRoleMapper, SsoRole> impl
}
return baseMapper.isTenantRole(roleId, tenantId) > 0;
}
@Override
public Result<List<String>> getRoleIdsByCode(String tenantId, List<String> roleCodes) {
return Result.ok(baseMapper.getRoleIdsByCode(tenantId, roleCodes), "通过角色编码获取角色ID成功");
}
}
@@ -327,6 +327,18 @@ public class SsoUserServiceImpl extends ServiceImpl<SsoUserMapper, SsoUser> impl
return Result.ok(list, "组织结构-查询成功!");
}
@Override
public Result<List<String>> getOrgIds(String tenantId, String userId, String direction) {
if (StringUtils.isEmpty(tenantId)) {
userId = AuthInfoUtils.getCurrentTenantId();
}
if (StringUtils.isEmpty(userId)) {
userId = AuthInfoUtils.getCurrentUserId();
}
SsoUser user = getUserById(userId);
return ssoOrgService.queryOrgIdsById(tenantId, user.getOrgIds(), TreeDirection.getDirection(direction));
}
@Override
public boolean isAccountExist(String account, String userId) {
return baseMapper.isAccountExist(account, userId) > 0;
@@ -334,7 +346,7 @@ public class SsoUserServiceImpl extends ServiceImpl<SsoUserMapper, SsoUser> impl
@Override
public int insertUserRole(String userId, List<String> roles) {
if (roles == null || roles.size() == 0) {
if (roles == null || roles.isEmpty()) {
return 0;
}
int count = baseMapper.insertUserRole(userId, roles);
@@ -149,7 +149,7 @@ org:
instanceId: AUTO
threadPool:
poolClass: org.quartz.simpl.SimpleThreadPool
threadCount: 50
threadCount: 10
threadPriority: 5
jobStore:
jdbcClass: org.quartz.impl.jdbcjobstore.JobStoreTX