feat:(REQ-2720) 增加菜单树缓存,修改基于菜单树缓存的鉴权

This commit is contained in:
lilong 2024-07-28 11:07:17 +08:00
parent 3ac9739644
commit dec1069f9e
14 changed files with 652 additions and 50 deletions

View File

@ -5,6 +5,7 @@ import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 权限点授权策略类型
@ -39,4 +40,8 @@ public enum DelegatedType {
public boolean sameCode(Integer code) {
return this.code.equals(code);
}
public static boolean notAuth(Integer delegatedType) {
return Objects.equals(NO_NEED.getCode(), delegatedType);
}
}

View File

@ -7,6 +7,7 @@ import cn.axzo.foundation.page.PageResp;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.req.CommonDictQueryReq;
import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq;
import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq;
@ -21,6 +22,9 @@ import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO;
import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO;
import cn.axzo.tyr.server.event.outer.CacheWorkspaceProductHandler;
import cn.axzo.tyr.server.event.payload.ServicePkgProductCreatedPayload;
import cn.axzo.tyr.server.job.CacheProductPermissionJob;
import cn.axzo.tyr.server.job.CacheRolePermissionJob;
import cn.axzo.tyr.server.job.CacheSaasFeatureJob;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao;
import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao;
@ -123,6 +127,12 @@ public class PrivateController {
private CacheWorkspaceProductHandler cacheWorkspaceProductHandler;
@Autowired
private SaasRoleGroupDao saasRoleGroupDao;
@Autowired
private CacheProductPermissionJob cacheProductPermissionJob;
@Autowired
private CacheRolePermissionJob cacheRolePermissionJob;
@Autowired
private CacheSaasFeatureJob cacheSaasFeatureJob;
/**
* 统一层级的roleGroup按照id升序sort从1递增
@ -578,8 +588,8 @@ public class PrivateController {
}
@PostMapping("/api/private/workspaceProduct/store")
public Object storeWorkspaceProduct(@RequestBody WorkspaceProductService.StoreWorkspaceProductParam request) {
workspaceProductService.storeWorkspaceProduct(request);
public Object storeWorkspaceProduct(@RequestBody ProductSearchListReq request) throws Exception {
cacheProductPermissionJob.execute(JSON.toJSONString(request));
return "ok";
}
@ -651,6 +661,26 @@ public class PrivateController {
return "ok";
}
@PostMapping("/api/private/rolePermission/store")
public Object storeRolePermission(@RequestBody RoleService.PageSaasRoleParam request) throws Exception {
cacheRolePermissionJob.execute(JSON.toJSONString(request));
return "ok";
}
@PostMapping("/api/private/saasFeature/store")
public Object storeSaasFeature(@RequestBody StoreFeatureParam request) throws Exception {
cacheSaasFeatureJob.execute(request.getTerminal());
return "ok";
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class StoreFeatureParam {
private String terminal;
}
@Data
@Builder
@NoArgsConstructor

View File

@ -81,7 +81,7 @@ public class CacheRolePermissionHandler implements EventHandler, InitializingBea
log.info("end cached role permission handler rocketmq event: {}", event);
}
@Override
@Override
public void afterPropertiesSet() throws Exception {
eventConsumer.registerHandler(EventTypeEnum.ROLE_PERMISSION_CREATED.getEventCode(), this);
}

View File

@ -0,0 +1,69 @@
package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.tyr.client.model.enums.DelegatedType;
import cn.axzo.tyr.server.event.payload.SaasFeatureUpsertPayload;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import cn.axzo.tyr.server.service.SaasFeatureService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 缓存全量权限点因为鉴权等逻辑需要查询免授权和权限点是否存在数据量大数据库压力大
*/
@Slf4j
@Component
public class CacheSaasFeatureHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Autowired
private SaasFeatureDao saasFeatureDao;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Override
public void onEvent(Event event, EventConsumer.Context context) {
log.info("begin cached saasFeature handler rocketmq event: {}", event);
SaasFeatureUpsertPayload payload = event.normalizedData(SaasFeatureUpsertPayload.class);
if (StringUtils.isBlank(payload.getTerminal())) {
return;
}
List<SaasFeatureResourceService.SaasFeatureResourceCache> saasFeatures = saasFeatureDao.lambdaQuery()
.eq(SaasFeature::getTerminal, payload.getTerminal())
.list()
.stream()
.map(e -> SaasFeatureResourceService.SaasFeatureResourceCache
.builder()
.featureId(e.getId())
.notAuth(DelegatedType.notAuth(e.getDelegatedType()))
.build())
.collect(Collectors.toList());
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.terminal(payload.getTerminal())
.features(saasFeatures)
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
log.info("end cached saasFeature handler rocketmq event: {}", event);
}
@Override
public void afterPropertiesSet() throws Exception {
eventConsumer.registerHandler(EventTypeEnum.SAAS_FEATURE_UPSERT.getEventCode(), this);
}
}

View File

@ -0,0 +1,66 @@
package cn.axzo.tyr.server.event.inner;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.server.event.payload.SaasFeatureResourceUpsertPayload;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 缓存全量权限点因为鉴权等逻辑需要查询免授权和权限点是否存在数据量大数据库压力大
*/
@Slf4j
@Component
public class CacheSaasFeatureResourceHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Override
public void onEvent(Event event, EventConsumer.Context context) {
log.info("begin cached saasFeatureResource handler rocketmq event: {}", event);
SaasFeatureResourceUpsertPayload payload = event.normalizedData(SaasFeatureResourceUpsertPayload.class);
if (StringUtils.isBlank(payload.getTerminal())) {
return;
}
// 直接查询缓存所有节点因为修改的代码不好改
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.terminal(payload.getTerminal())
.build();
List<SaasFeatureResourceService.SaasFeatureResourceCache> saasFeatures = saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream()
.map(e -> SaasFeatureResourceService.SaasFeatureResourceCache.builder()
.featureId(e.getId())
.notAuth(SaasFeatureResource.AuthType.isAllRole(e.getAuthType()))
.build())
.collect(Collectors.toList());
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.terminal(payload.getTerminal())
.features(saasFeatures)
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
log.info("end cached saasFeatureResource handler rocketmq event: {}", event);
}
@Override
public void afterPropertiesSet() throws Exception {
eventConsumer.registerHandler(EventTypeEnum.SAAS_FEATURE_RESOURCE_UPSERT.getEventCode(), this);
}
}

View File

@ -6,7 +6,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Set;
@Data
@Builder
@ -14,5 +13,5 @@ import java.util.Set;
@AllArgsConstructor
public class SaasFeatureResourceUpsertPayload implements Serializable {
private Set<Long> ids;
private String terminal;
}

View File

@ -6,13 +6,14 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Set;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SaasFeatureUpsetPayload implements Serializable {
public class SaasFeatureUpsertPayload implements Serializable {
private Set<Long> ids;
private Long id;
private String terminal;
}

View File

@ -0,0 +1,54 @@
package cn.axzo.tyr.server.job;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.tyr.client.model.product.ProductSearchListReq;
import cn.axzo.tyr.client.model.product.ProductVO;
import cn.axzo.tyr.server.event.inner.CacheProductPermissionHandler;
import cn.axzo.tyr.server.event.payload.ProductPermissionCreatedPayload;
import cn.axzo.tyr.server.service.ProductService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class CacheProductPermissionJob extends IJobHandler {
@Autowired
private CacheProductPermissionHandler cacheProductPermissionHandler;
@Autowired
private ProductService productService;
@Override
@XxlJob("CacheProductPermissionJob")
public ReturnT<String> execute(String s) throws Exception {
log.info("start CacheProductPermissionJob, s:{}", s);
ProductSearchListReq productSearchListReq = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, ProductSearchListReq.class))
.orElseGet(ProductSearchListReq::new);
Set<Long> productIds = productService.list(productSearchListReq).getData()
.stream()
.map(ProductVO::getId)
.collect(Collectors.toSet());
ProductPermissionCreatedPayload payload = ProductPermissionCreatedPayload.builder()
.productModuleIds(productIds)
.build();
Event event = Event.builder()
.data(payload)
.build();
cacheProductPermissionHandler.onEvent(event, null);
return ReturnT.SUCCESS;
}
}

View File

@ -0,0 +1,68 @@
package cn.axzo.tyr.server.job;
import cn.axzo.foundation.page.PageResp;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.tyr.client.model.res.SaasRoleRes;
import cn.axzo.tyr.server.event.inner.CacheRolePermissionHandler;
import cn.axzo.tyr.server.event.payload.RolePermissionCreatedPayload;
import cn.axzo.tyr.server.service.RoleService;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Component
public class CacheRolePermissionJob extends IJobHandler {
@Autowired
private CacheRolePermissionHandler cacheRolePermissionHandler;
@Autowired
private RoleService roleService;
private static final Integer DEFAULT_PAGE_SIZE = 1000;
@Override
@XxlJob("CacheRolePermissionJob")
public ReturnT<String> execute(String s) throws Exception {
log.info("start CacheRolePermissionJob, s:{}", s);
RoleService.PageSaasRoleParam pageSaasRoleParam = Optional.ofNullable(s)
.map(e -> JSONObject.parseObject(e, RoleService.PageSaasRoleParam.class))
.orElseGet(() -> RoleService.PageSaasRoleParam.builder().build());
Integer pageNumber = 1;
while (true) {
pageSaasRoleParam.setPage(pageNumber++);
pageSaasRoleParam.setPageSize(DEFAULT_PAGE_SIZE);
PageResp<SaasRoleRes> page = roleService.page(pageSaasRoleParam);
Set<Long> roleIds = page.getData().stream()
.map(SaasRoleRes::getId)
.collect(Collectors.toSet());
RolePermissionCreatedPayload payload = RolePermissionCreatedPayload.builder()
.roleIds(roleIds)
.build();
Event event = Event.builder()
.data(payload)
.build();
cacheRolePermissionHandler.onEvent(event, null);
if (!page.hasNext()) {
break;
}
}
return ReturnT.SUCCESS;
}
}

View File

@ -0,0 +1,86 @@
package cn.axzo.tyr.server.job;
import cn.axzo.tyr.client.model.enums.DelegatedType;
import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq;
import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.entity.SaasFeature;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import cn.axzo.tyr.server.service.SaasFeatureResourceService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Component
public class CacheSaasFeatureJob extends IJobHandler {
@Autowired
private SaasFeatureDao saasFeatureDao;
@Autowired
private SaasFeatureResourceService saasFeatureResourceService;
@Override
@XxlJob("CacheSaasFeatureJob")
public ReturnT<String> execute(String s) throws Exception {
log.info("start CacheSaasFeatureJob, s:{}", s);
cacheSaasFeature(s);
cacheSaasFeatureResource(s);
return ReturnT.SUCCESS;
}
private void cacheSaasFeature(String terminal) {
Map<String, List<SaasFeatureResourceService.SaasFeatureResourceCache>> saasFeatures = saasFeatureDao.lambdaQuery()
.eq(StringUtils.isNotBlank(terminal), SaasFeature::getTerminal, terminal)
.list()
.stream()
.collect(Collectors.groupingBy(SaasFeature::getTerminal,
Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache
.builder()
.featureId(e.getId())
.notAuth(DelegatedType.notAuth(e.getDelegatedType()))
.build(), Collectors.toList())));
saasFeatures.entrySet().forEach(e -> {
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.terminal(e.getKey())
.features(e.getValue())
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
});
}
private void cacheSaasFeatureResource(String terminal) {
PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder()
.terminal(terminal)
.build();
Map<String, List<SaasFeatureResourceService.SaasFeatureResourceCache>> saasFeatureResources = saasFeatureResourceService.list(pageSaasFeatureResourceReq)
.stream()
.collect(Collectors.groupingBy(SaasFeatureResourceResp::getTerminal,
Collectors.mapping(e -> SaasFeatureResourceService.SaasFeatureResourceCache
.builder()
.featureId(e.getId())
.notAuth(SaasFeatureResource.AuthType.isAllRole(e.getAuthType()))
.build(), Collectors.toList())));
saasFeatureResources.entrySet().forEach(e -> {
SaasFeatureResourceService.StoreSaasFeatureResourceCache storeSaasFeatureResourceCache = SaasFeatureResourceService.StoreSaasFeatureResourceCache.builder()
.terminal(e.getKey())
.features(e.getValue())
.build();
saasFeatureResourceService.storeCache(storeSaasFeatureResourceCache);
});
}
}

View File

@ -11,8 +11,13 @@ import cn.axzo.tyr.server.model.ResourcePermission;
import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO;
import cn.axzo.tyr.server.repository.entity.SaasFeatureResource;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@ -59,4 +64,38 @@ public interface SaasFeatureResourceService extends IService<SaasFeatureResource
void deleteFeatureResource(DeleteFeatureResourceReq param);
void storeCache(StoreSaasFeatureResourceCache param);
Map<String, List<SaasFeatureResourceCache>> listCache(ListSaasFeatureResourceCache param);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class StoreSaasFeatureResourceCache {
private String terminal;
private List<SaasFeatureResourceCache> features;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ListSaasFeatureResourceCache {
private Set<String> terminals;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class SaasFeatureResourceCache {
private Long featureId;
private boolean notAuth;
}
}

View File

@ -1,37 +1,29 @@
package cn.axzo.tyr.server.service.impl;
import static cn.axzo.tyr.client.model.enums.FeatureType.BUTTON;
import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE;
import static cn.axzo.tyr.server.common.constants.PermissionConstant.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import cn.axzo.tyr.client.model.enums.FeatureDataType;
import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq;
import cn.axzo.tyr.client.model.res.SimplePermissionPointResp;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Opt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.basics.common.util.StopWatchUtil;
import cn.axzo.basics.common.util.TreeUtil;
import cn.axzo.framework.domain.web.code.BaseCode;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.tyr.client.model.dict.request.BasicDictNodeReq;
import cn.axzo.tyr.client.model.dict.request.BasicDictQueryReq;
import cn.axzo.tyr.client.model.dict.response.BasicDictNodeResp;
import cn.axzo.tyr.client.model.dict.response.BasicDictTreeResp;
import cn.axzo.tyr.client.model.enums.DictTypeFiledEnum;
import cn.axzo.tyr.client.model.enums.DictWorkSpaceTypeEnum;
import cn.axzo.tyr.client.model.enums.FeatureDataType;
import cn.axzo.tyr.client.model.enums.FeatureType;
import cn.axzo.tyr.client.model.permission.*;
import cn.axzo.tyr.client.model.permission.PermissionPointDTO;
import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest;
import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest;
import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode;
import cn.axzo.tyr.client.model.permission.PermissionPointTreeQueryReq;
import cn.axzo.tyr.client.model.permission.PermissionPointVO;
import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq;
import cn.axzo.tyr.client.model.res.SimplePermissionPointResp;
import cn.axzo.tyr.server.common.util.Throws;
import cn.axzo.tyr.server.config.MqProducer;
import cn.axzo.tyr.server.event.payload.SaasFeatureUpsertPayload;
import cn.axzo.tyr.server.repository.dao.SaasFeatureDao;
import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao;
import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao;
@ -40,10 +32,32 @@ import cn.axzo.tyr.server.service.PermissionPointService;
import cn.axzo.tyr.server.service.SaasBasicDictService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.tyr.client.model.enums.FeatureType.BUTTON;
import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE;
import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_BIZ_NO_PREFIX;
import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_PATH_DELIMITER;
import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_BIZ_NO;
import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_PATH;
import static cn.axzo.tyr.server.event.inner.EventTypeEnum.SAAS_FEATURE_UPSERT;
/**
* 权限点服务实现
@ -61,6 +75,9 @@ public class PermissionPointServiceImpl implements PermissionPointService {
private final SaasBasicDictService saasBasicDictService;
private final SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao;
private final SaasProductModuleFeatureRelationDao saasProductModuleFeatureRelationDao;
private final MqProducer mqProducer;
private static final String TARGET_TYPE = "saasFeatureId";
@Override
public List<PermissionPointTreeNode> listTreeNodes(PermissionPointTreeQueryReq request) {
@ -333,6 +350,11 @@ public class PermissionPointServiceImpl implements PermissionPointService {
this.saasFeatureDao.updateById(saasFeature);
//返回一些要用的数据
dto.setBusinessNo(feature.getBusinessNo());
sendMsg(SaasFeatureUpsertPayload.builder()
.id(dto.getId())
.terminal(feature.getTerminal())
.build());
return dto;
}
@ -367,6 +389,11 @@ public class PermissionPointServiceImpl implements PermissionPointService {
dto.setBusinessNo(saasFeature.getBusinessNo());
//调整排序 - 兼容处理老数据数据规范化
changeSort(saasFeature, saasFeature.getSort());
sendMsg(SaasFeatureUpsertPayload.builder()
.id(dto.getId())
.terminal(saasFeature.getTerminal())
.build());
return dto;
}
@ -426,6 +453,11 @@ public class PermissionPointServiceImpl implements PermissionPointService {
//删除关联数据
this.saasPgroupPermissionRelationDao.removeByPermissionPointIds(delIds);
this.saasProductModuleFeatureRelationDao.removeByPermissionPointIds(delIds);
sendMsg(SaasFeatureUpsertPayload.builder()
.id(feature.getId())
.terminal(feature.getTerminal())
.build());
return bizNoList;
}
@ -643,4 +675,12 @@ public class PermissionPointServiceImpl implements PermissionPointService {
.build();
}
private void sendMsg(SaasFeatureUpsertPayload saasFeatureUpsertPayload) {
Event event = Event.builder()
.targetType(TARGET_TYPE)
.eventCode(SAAS_FEATURE_UPSERT.getEventCode())
.data(saasFeatureUpsertPayload)
.build();
mqProducer.send(event);
}
}

View File

@ -10,6 +10,7 @@ import cn.axzo.foundation.page.PageResp;
import cn.axzo.framework.domain.web.code.BaseCode;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.pokonyan.config.mybatisplus.BaseEntity;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.axzo.tyr.client.common.enums.FeatureResourceAuthType;
import cn.axzo.tyr.client.common.enums.FeatureResourceStatus;
import cn.axzo.tyr.client.common.enums.FeatureResourceType;
@ -44,18 +45,28 @@ import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService;
import cn.azxo.framework.common.utils.StringUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -65,6 +76,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static cn.axzo.tyr.server.config.exception.BizResultCode.FEATURE_RESOURCE_NOT_FOUND;
@ -95,6 +107,15 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
private final SaasPageElementFeatureResourceRelationService saasPageElementFeatureResourceRelationService;
private final MqProducer mqProducer;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/** 菜单树过期时间 **/
@Value("${saas.feature.resource.expire:180}")
private Long expireInDays;
private static final String SAAS_FEATURE_RESOURCE_KEY = "saas:feature:resource:%s";
private static final String TARGET_TYPE = "saasFeatureResourceId";
@Override
public List<SaasFeatureResource> listNavByIds(List<Long> featureIds, List<Integer> featureTypes) {
@ -267,7 +288,7 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
.targetType(TARGET_TYPE)
.eventCode(SAAS_FEATURE_RESOURCE_UPSERT.getEventCode())
.data(SaasFeatureResourceUpsertPayload.builder()
.ids(mqUpsertIds)
.terminal(req.getTerminal())
.build())
.build();
mqProducer.send(event);
@ -622,6 +643,15 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
.collect(Collectors.toList()));
deletePermissionRelations(deleteFeatureResource);
Event event = Event.builder()
.targetType(TARGET_TYPE)
.eventCode(SAAS_FEATURE_RESOURCE_UPSERT.getEventCode())
.data(SaasFeatureResourceUpsertPayload.builder()
.terminal(featureResource.getTerminal())
.build())
.build();
mqProducer.send(event);
}
private void deletePermissionRelations(List<SaasFeatureResourceResp> deleteFeatureResource) {
@ -671,4 +701,43 @@ public class SaasFeatureResourceServiceImpl extends ServiceImpl<SaasFeatureResou
recursiveSetPageElement(e.getChildren(), uniCodeElementsMap);
});
}
@Override
public void storeCache(StoreSaasFeatureResourceCache param) {
RedisClient.StringOps.setEx(getKey(param.getTerminal()), JSONObject.toJSONString(param.getFeatures()),
expireInDays, TimeUnit.DAYS);
}
@Override
public Map<String, List<SaasFeatureResourceCache>> listCache(ListSaasFeatureResourceCache param) {
List<Object> redisValues = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
for (String terminal : param.getTerminals()) {
String redisKey = getKey(terminal);
RedisClient.StringOps.get(redisKey);
}
return null;
}
});
return Streams.zip(param.getTerminals().stream(),
redisValues.stream(),
(terminal, redisValue) -> {
if (Objects.isNull(redisValue)) {
return null;
}
return Pair.of(terminal, JSONArray.parseArray((String) redisValue,
SaasFeatureResourceCache.class));
})
.filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}
private String getKey(Object... params) {
return String.format(SAAS_FEATURE_RESOURCE_KEY, params);
}
}

View File

@ -2,6 +2,7 @@ package cn.axzo.tyr.server.service.impl;
import cn.axzo.apollo.workspace.common.enums.TableIsDeleteEnum;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.framework.domain.ServiceException;
import cn.axzo.pokonyan.util.TraceSupplier;
import cn.axzo.thrones.client.saas.ServicePkgClient;
import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct;
@ -10,7 +11,6 @@ import cn.axzo.tyr.client.common.enums.RoleTypeEnum;
import cn.axzo.tyr.client.common.enums.WorkspaceJoinType;
import cn.axzo.tyr.client.model.enums.DelegatedType;
import cn.axzo.tyr.client.model.enums.IdentityType;
import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest;
import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode;
import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO;
import cn.axzo.tyr.client.model.req.CheckIdentityPermissionReq;
@ -447,8 +447,52 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
private String terminal;
}
private Set<Long> checkFeatureCodes(ListPermissionUser param) {
// 查询免授权的featureCodes判断featureCodes里是否有免授权的featureCodes
// 兼容新老版本需要通过featureCode查询新版本的features原逻辑是查询当前菜单资源的所有子数据
List<SaasFeature> features = permissionPointService.listNodeWithChildrenByCodes(Lists.newArrayList(param.getFeatureCodes()), param.getTerminal());
ListSaasFeatureResourceParam listSaasFeatureResourceParam = ListSaasFeatureResourceParam.builder()
.featureCodes(param.getFeatureCodes())
.terminal(param.getTerminal())
.build();
List<SaasFeatureResourceResp> saasFeatureResources = listSaasFeatureResource(listSaasFeatureResourceParam);
if (CollectionUtil.isEmpty(features) && CollectionUtils.isEmpty(saasFeatureResources)) {
log.warn("no features data found for:{}", param.getFeatureCodes());
return Collections.emptySet();
}
//是否免授权权限点
Optional<SaasFeature> freeFeature = features.stream()
.filter(f -> DelegatedType.NO_NEED.sameCode(f.getDelegatedType()))
.findAny();
if (freeFeature.isPresent()) {
throw new ServiceException("不能查询免授权权限点人员");
}
Optional<SaasFeatureResourceResp> freeFeatureResource = saasFeatureResources.stream()
.filter(e -> SaasFeatureResource.AuthType.isAllRole(e.getAuthType()))
.findFirst();
if (freeFeatureResource.isPresent()) {
throw new ServiceException("不能查询免授权权限点人员");
}
Set<Long> allFeatureIds = Sets.newHashSet();
allFeatureIds.addAll(features.stream()
.map(SaasFeature::getId)
.collect(Collectors.toSet()));
allFeatureIds.addAll(saasFeatureResources.stream()
.map(SaasFeatureResourceResp::getId)
.collect(Collectors.toSet()));
return allFeatureIds;
}
private List<ListIdentityFromPermissionResp.UserVO> listPermissionUser(ListPermissionUser param) {
// TODO 查询免授权的featureCodes判断featureCodes里是否有免授权的featureCodes
// 查询的featureCodes有可能已经被删除但是saas_feature_code效率比较低两个表的id不会重复所以解析成id去过滤权限
Set<Long> featureIds = checkFeatureCodes(param);
WorkspaceProductService.ListWorkspaceProductPermissionCacheParam listWorkspaceProductPermissionCacheParam = WorkspaceProductService.ListWorkspaceProductPermissionCacheParam.builder()
.workspaceIds(Sets.newHashSet(param.getWorkspaceId()))
@ -462,6 +506,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
.map(WorkspaceProductService.ProductPermission::getPermissions)
.filter(e -> !CollectionUtils.isEmpty(e))
.flatMap(Collection::stream)
.filter(e -> featureIds.contains(e.getFeatureId()))
.filter(e -> StringUtils.isBlank(param.getTerminal()) || Objects.equals(e.getTerminal(), param.getTerminal()))
.collect(Collectors.toList());
@ -470,7 +515,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
return Collections.emptyList();
}
return getWorkspaceUserV2(param, productPermissions);
return getWorkspaceUserV2(param, productPermissions, featureIds);
}
@Override
@ -520,13 +565,17 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
}
private Set<Long> resolvePermissionNormalRole(ListPermissionUser req,
List<ProductPermissionCacheService.PermissionDTO> productPermissions) {
List<ProductPermissionCacheService.PermissionDTO> productPermissions,
Set<Long> featureIds) {
// 因为通过权限id找对应的角色数据量巨大所以通过找项目的角色再找有权限的角色比较快
Set<Long> allRoleIds = saasRoleUserRelationMapper.listRoleIds(SaasRoleUserRelationMapper.ListRole.builder()
.ouId(req.getOuId())
.workspaceId(req.getWorkspaceId())
.build());
if (CollectionUtils.isEmpty(allRoleIds)) {
return Collections.emptySet();
}
RolePermissionCacheService.ListRolePermissionParam listRolePermissionParam = RolePermissionCacheService.ListRolePermissionParam.builder()
.roleIds(allRoleIds)
@ -538,6 +587,7 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
Set<Long> normalRoleIds = normalRolePermissionMap.entrySet().stream()
.filter(e -> !CollectionUtils.isEmpty(e.getValue()))
.filter(e -> e.getValue().stream().anyMatch(p -> featureIds.contains(p.getFeatureId())))
.filter(e -> StringUtils.isBlank(req.getTerminal())
|| e.getValue().stream().anyMatch(p -> Objects.equals(p.getTerminal(), req.getTerminal())))
.map(Map.Entry::getKey)
@ -574,13 +624,14 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
}
private List<ListIdentityFromPermissionResp.UserVO> getWorkspaceUserV2(ListPermissionUser req,
List<ProductPermissionCacheService.PermissionDTO> productPermissions) {
List<ProductPermissionCacheService.PermissionDTO> productPermissions,
Set<Long> featureIds) {
//超管和管理员
List<SaasRoleRes> adminRoles = listAdminRole(req);
Set<Long> adminPermissionRoleIds = resolvePermissionAdminRole(adminRoles, productPermissions);
Set<Long> normalPermissionRoleIds = resolvePermissionNormalRole(req, productPermissions);
Set<Long> normalPermissionRoleIds = resolvePermissionNormalRole(req, productPermissions, featureIds);
Set<Long> roleIds = Sets.newHashSet();
roleIds.addAll(adminPermissionRoleIds);
@ -1444,6 +1495,8 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
result.setPermissions(permissions);
return result;
}
private IdentityAuthRes.WorkspacePermission buildPermissionsV2(IdentityAuthReq.WorkspaceOuPair workspaceOuPair,
List<ProductPermissionCacheService.PermissionDTO> productPermissions,
@ -1455,11 +1508,22 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
.ouId(workspaceOuPair.getOuId())
.build();
// 根据项目的产品找到对应端的所有权限点因为可能存在某些权限点被删除或者是免授权虽然有两颗权限点树但是id不会重复新的权限点从100000开始历史的后续不会再使用
Set<String> terminals = productPermissions.stream()
.map(ProductPermissionCacheService.PermissionDTO::getTerminal)
.collect(Collectors.toSet());
List<SaasFeatureResourceService.SaasFeatureResourceCache> allFeatures = listSaasFeatureCaches(terminals);
if (CollectionUtils.isEmpty(allFeatures)) {
return workspacePermission;
}
//超管和管理员权限
Set<IdentityAuthRes.PermissionPoint> adminPermissionPoints = buildAdminPermissionV2(productPermissions, saasRoles);
Set<IdentityAuthRes.PermissionPoint> noAuthPermissionPoints = buildNoAuthPermission(productPermissions);
Set<IdentityAuthRes.PermissionPoint> noAuthPermissionPoints = buildNoAuthPermission(productPermissions, allFeatures);
Set<IdentityAuthRes.PermissionPoint> normalPermissionPoints = buildNormalPermissionV2(productPermissions, saasRoles, rolePermissions);
//组装返回值
//是否超管
boolean superAdmin = saasRoles.stream()
@ -1470,12 +1534,28 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
allPermissionPoints.addAll(adminPermissionPoints);
allPermissionPoints.addAll(noAuthPermissionPoints);
allPermissionPoints.addAll(normalPermissionPoints);
// TODO 过滤掉已经删除的权限点
// 过滤掉已经删除的权限点
Set<Long> effectFeatureIds = allFeatures.stream()
.map(SaasFeatureResourceService.SaasFeatureResourceCache::getFeatureId)
.collect(Collectors.toSet());
workspacePermission.setPermissionPoint(Lists.newArrayList(allPermissionPoints));
workspacePermission.setPermissionPoint(allPermissionPoints.stream()
.filter(e -> effectFeatureIds.contains(e.getFeatureId()))
.collect(Collectors.toList()));
return workspacePermission;
}
private List<SaasFeatureResourceService.SaasFeatureResourceCache> listSaasFeatureCaches(Set<String> terminals) {
SaasFeatureResourceService.ListSaasFeatureResourceCache listSaasFeatureResourceCache = SaasFeatureResourceService.ListSaasFeatureResourceCache.builder()
.terminals(terminals)
.build();
return saasFeatureResourceService.listCache(listSaasFeatureResourceCache)
.values()
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
private Set<IdentityAuthRes.PermissionPoint> buildAdminPermissionV2(List<ProductPermissionCacheService.PermissionDTO> productPermissions,
List<SaasRoleUserV2DTO.SaasRole> saasRoles) {
//超管和管理员角色
@ -1506,23 +1586,19 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService {
}
private Set<IdentityAuthRes.PermissionPoint> buildNoAuthPermission(List<ProductPermissionCacheService.PermissionDTO> productPermissions) {
// 旧的菜单树免授权的节点很少所以直接查询线上只有11个
Set<String> noAuthSaasFeatureCodes = permissionPointService.queryList(PermissionPointListQueryRequest.builder()
.delegatedType(DelegatedType.NO_NEED.getCode())
.build())
.stream()
.map(PermissionPointTreeNode::getCode)
private Set<IdentityAuthRes.PermissionPoint> buildNoAuthPermission(List<ProductPermissionCacheService.PermissionDTO> productPermissions,
List<SaasFeatureResourceService.SaasFeatureResourceCache> allFeatuers) {
Set<Long> notAuthFeatureIds = allFeatuers.stream()
.filter(SaasFeatureResourceService.SaasFeatureResourceCache::isNotAuth)
.map(SaasFeatureResourceService.SaasFeatureResourceCache::getFeatureId)
.collect(Collectors.toSet());
// TODO 查询新权限树免授权的权限点
if (CollectionUtils.isEmpty(noAuthSaasFeatureCodes)) {
if (CollectionUtils.isEmpty(notAuthFeatureIds)) {
return Collections.emptySet();
}
return productPermissions.stream()
.filter(productPermission -> noAuthSaasFeatureCodes.contains(productPermission.getFeatureCode()))
.filter(productPermission -> notAuthFeatureIds.contains(productPermission.getFeatureId()))
.map(e -> IdentityAuthRes.PermissionPoint.builder()
.featureCode(e.getFeatureCode())
.featureId(e.getFeatureId())