支持单位授权与客户端限制

This commit is contained in:
Codex
2026-06-01 11:58:14 +08:00
parent 28adc8143a
commit c136cc2cc8
36 changed files with 861 additions and 49 deletions

View File

@@ -1,6 +1,7 @@
package com.xspaceagi.system.application.dto.permission;
import java.io.Serializable;
import java.util.Date;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -20,6 +21,15 @@ public class SysGroupAddDto implements Serializable {
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "最大客户端数,-1表示不限制")
private Integer maxClientCount;
@Schema(description = "授权开关1-已授权0-未授权")
private Integer authEnabled;
@Schema(description = "授权到期时间,空表示不过期")
private Date expireTime;
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
private Integer source;

View File

@@ -31,6 +31,15 @@ public class SysGroupDto implements Serializable {
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "最大客户端数,-1表示不限制")
private Integer maxClientCount;
@Schema(description = "授权开关1-已授权0-未授权")
private Integer authEnabled;
@Schema(description = "授权到期时间,空表示不过期")
private Date expireTime;
@Schema(description = "来源,1:系统内置 2:用户自定义")
private Integer source;

View File

@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class SysGroupUpdateDto implements Serializable {
@@ -23,6 +24,15 @@ public class SysGroupUpdateDto implements Serializable {
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "最大客户端数,-1表示不限制")
private Integer maxClientCount;
@Schema(description = "授权开关1-已授权0-未授权")
private Integer authEnabled;
@Schema(description = "授权到期时间,空表示不过期")
private Date expireTime;
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
private Integer source;

View File

@@ -76,6 +76,16 @@ public interface SysGroupApplicationService {
*/
List<SysGroup> getEffectiveGroupListByUserId(Long userId);
/**
* 查询用户可用于系统登录的已授权用户组(启用、授权开启、未过期)
*/
List<SysGroup> getAuthorizedGroupListForLogin(Long userId);
/**
* 为用户选择一个可注册客户端的已授权用户组(同时满足客户端数量限制)
*/
SysGroup selectAuthorizedGroupForClient(Long userId, java.util.Map<Long, Long> clientCountByGroupId);
/**
* 用户组绑定用户(全量覆盖)
*/

View File

@@ -3,13 +3,16 @@ package com.xspaceagi.system.application.service.impl;
import com.xspaceagi.system.application.dto.TenantConfigDto;
import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.application.service.AuthService;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
import com.xspaceagi.system.application.service.UserApplicationService;
import com.xspaceagi.system.infra.dao.entity.User;
import com.xspaceagi.system.infra.rpc.WeChatMpService;
import com.xspaceagi.system.infra.verify.VerifyCodeSendAndCheckService;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.enums.CodeTypeEnum;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.spec.utils.I18nUtil;
import com.xspaceagi.system.spec.utils.JwtUtils;
import com.xspaceagi.system.spec.utils.RedisUtil;
@@ -28,6 +31,9 @@ public class AuthServiceImpl implements AuthService {
@Resource
private UserApplicationService userApplicationService;
@Resource
private SysGroupApplicationService sysGroupApplicationService;
@Resource
private VerifyCodeSendAndCheckService verifyCodeSendAndCheckService;
@@ -66,9 +72,7 @@ public class AuthServiceImpl implements AuthService {
userDto.setEmail(email);
userApplicationService.add(userDto);
}
if (userDto.getStatus() == User.Status.Disabled || userDto.getStatus() == User.Status.Deleted) {
throw new BizException(I18nUtil.systemMessage("Backend.Auth.Login.AccountDisabled"));
}
validateLoginUser(userDto);
updateLastLoginTime(userDto);
return createToken(userDto, UUID.randomUUID().toString().replace("-", ""));
}
@@ -87,13 +91,23 @@ public class AuthServiceImpl implements AuthService {
userDto.setPhone(phone);
userApplicationService.add(userDto);
}
if (userDto.getStatus() == User.Status.Disabled) {
throw new BizException(I18nUtil.systemMessage("Backend.Auth.Login.AccountDisabled"));
}
validateLoginUser(userDto);
updateLastLoginTime(userDto);
return createToken(userDto, UUID.randomUUID().toString().replace("-", ""));
}
private void validateLoginUser(UserDto userDto) {
if (userDto.getStatus() == User.Status.Disabled || userDto.getStatus() == User.Status.Deleted) {
throw new BizException(I18nUtil.systemMessage("Backend.Auth.Login.AccountDisabled"));
}
if (userDto.getRole() == User.Role.Admin) {
return;
}
if (sysGroupApplicationService.getAuthorizedGroupListForLogin(userDto.getId()).isEmpty()) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.systemGroupAuthorizationUnavailable);
}
}
private void updateLastLoginTime(UserDto userDto) {
UserDto update = new UserDto();
update.setId(userDto.getId());
@@ -105,6 +119,7 @@ public class AuthServiceImpl implements AuthService {
}
public String createToken(UserDto userDto, String clientId) {
validateLoginUser(userDto);
//过期token检查
Map<String, Object> map = redisUtil.hashGetAll("user-token:" + userDto.getId());
for (Map.Entry<String, Object> entry : map.entrySet()) {
@@ -127,6 +142,7 @@ public class AuthServiceImpl implements AuthService {
Assert.notNull(emailOrPhone, "emailOrPhone must be non-null");
UserDto userDto = userApplicationService.queryUserByPhoneOrEmailWithPassword(emailOrPhone, password);
if (userDto != null) {
validateLoginUser(userDto);
updateLastLoginTime(userDto);
return createToken(userDto, UUID.randomUUID().toString().replace("-", ""));
}

View File

@@ -2,9 +2,11 @@ package com.xspaceagi.system.application.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -23,6 +25,7 @@ import com.xspaceagi.system.domain.model.GroupBindMenuModel;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.domain.model.SortIndex;
import com.xspaceagi.system.domain.service.SysGroupDomainService;
import com.xspaceagi.system.domain.service.impl.GroupAuthorizationPolicy;
import com.xspaceagi.system.infra.dao.entity.SysDataPermission;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.User;
@@ -141,6 +144,39 @@ public class SysGroupApplicationServiceImpl implements SysGroupApplicationServic
: groupList.stream().filter(g -> StatusEnum.isEnabled(g.getStatus())).toList();
}
@Override
public List<SysGroup> getAuthorizedGroupListForLogin(Long userId) {
return filterAuthorizedGroupsForLogin(getEffectiveGroupListByUserId(userId));
}
@Override
public SysGroup selectAuthorizedGroupForClient(Long userId, Map<Long, Long> clientCountByGroupId) {
return selectAuthorizedGroupForClient(getEffectiveGroupListByUserId(userId), clientCountByGroupId);
}
List<SysGroup> filterAuthorizedGroupsForLogin(List<SysGroup> groups) {
if (CollectionUtils.isEmpty(groups)) {
return List.of();
}
Date now = new Date();
return groups.stream()
.filter(group -> GroupAuthorizationPolicy.evaluate(group, -1, now).allowed())
.toList();
}
SysGroup selectAuthorizedGroupForClient(List<SysGroup> groups, Map<Long, Long> clientCountByGroupId) {
if (CollectionUtils.isEmpty(groups)) {
return null;
}
Date now = new Date();
Map<Long, Long> counts = clientCountByGroupId == null ? Map.of() : clientCountByGroupId;
return groups.stream()
.filter(group -> group.getId() != null)
.filter(group -> GroupAuthorizationPolicy.evaluate(group, counts.getOrDefault(group.getId(), 0L), now).allowed())
.findFirst()
.orElse(null);
}
private List<Long> getSubscriptionPlanGroupIds(Long userId) {
try {
UserSubscriptionDTO subscription = subscriptionRpcService.getUserCurrentSystemSubscription(userId);

View File

@@ -0,0 +1,58 @@
package com.xspaceagi.system.application.service.impl;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.spec.enums.StatusEnum;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
class SysGroupApplicationServiceImplAuthorizationTest {
@Test
void filtersGroupsWithoutActiveAuthorizationForLogin() {
SysGroupApplicationServiceImpl service = new SysGroupApplicationServiceImpl();
List<SysGroup> result = service.filterAuthorizedGroupsForLogin(List.of(
group(1L, 0, future(), -1),
group(2L, 1, past(), -1),
group(3L, 1, future(), -1)
));
assertThat(result).extracting(SysGroup::getId).containsExactly(3L);
}
@Test
void selectsAuthorizedGroupWhoseClientQuotaIsAvailable() {
SysGroupApplicationServiceImpl service = new SysGroupApplicationServiceImpl();
SysGroup result = service.selectAuthorizedGroupForClient(List.of(
group(1L, 1, future(), 1),
group(2L, 1, future(), 2)
), Map.of(1L, 1L, 2L, 1L));
assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo(2L);
}
private SysGroup group(Long id, Integer authEnabled, Date expireTime, Integer maxClientCount) {
SysGroup group = new SysGroup();
group.setId(id);
group.setStatus(StatusEnum.ENABLED.getCode());
group.setAuthEnabled(authEnabled);
group.setExpireTime(expireTime);
group.setMaxClientCount(maxClientCount);
return group;
}
private Date future() {
return new Date(System.currentTimeMillis() + 86_400_000L);
}
private Date past() {
return new Date(System.currentTimeMillis() - 86_400_000L);
}
}

View File

@@ -0,0 +1,55 @@
package com.xspaceagi.system.domain.service.impl;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.spec.enums.StatusEnum;
import java.util.Date;
/**
* Centralized department authorization rule for login and client registration.
*/
public final class GroupAuthorizationPolicy {
private GroupAuthorizationPolicy() {
}
public static Result evaluate(SysGroup group, long currentClientCount, Date now) {
if (group == null) {
return Result.deny(Reason.NO_GROUP);
}
if (!StatusEnum.isEnabled(group.getStatus())) {
return Result.deny(Reason.GROUP_DISABLED);
}
if (!Integer.valueOf(1).equals(group.getAuthEnabled())) {
return Result.deny(Reason.AUTH_DISABLED);
}
Date expireTime = group.getExpireTime();
if (expireTime != null && !expireTime.after(now)) {
return Result.deny(Reason.EXPIRED);
}
Integer maxClientCount = group.getMaxClientCount();
if (maxClientCount != null && maxClientCount > -1 && currentClientCount >= maxClientCount) {
return Result.deny(Reason.CLIENT_QUOTA_REACHED);
}
return Result.allow();
}
public enum Reason {
ALLOWED,
NO_GROUP,
GROUP_DISABLED,
AUTH_DISABLED,
EXPIRED,
CLIENT_QUOTA_REACHED
}
public record Result(boolean allowed, Reason reason) {
private static Result allow() {
return new Result(true, Reason.ALLOWED);
}
private static Result deny(Reason reason) {
return new Result(false, reason);
}
}
}

View File

@@ -0,0 +1,90 @@
package com.xspaceagi.system.domain.service.impl;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.spec.enums.StatusEnum;
import org.junit.jupiter.api.Test;
import java.util.Calendar;
import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
class GroupAuthorizationPolicyTest {
@Test
void allowsEnabledAuthorizedGroupBeforeExpirationWhenClientQuotaAvailable() {
SysGroup group = group(StatusEnum.ENABLED.getCode(), 1, future(), 2);
GroupAuthorizationPolicy.Result result = GroupAuthorizationPolicy.evaluate(group, 1, now());
assertThat(result.allowed()).isTrue();
assertThat(result.reason()).isEqualTo(GroupAuthorizationPolicy.Reason.ALLOWED);
}
@Test
void deniesDisabledGroup() {
SysGroup group = group(StatusEnum.DISABLED.getCode(), 1, future(), 2);
GroupAuthorizationPolicy.Result result = GroupAuthorizationPolicy.evaluate(group, 0, now());
assertThat(result.allowed()).isFalse();
assertThat(result.reason()).isEqualTo(GroupAuthorizationPolicy.Reason.GROUP_DISABLED);
}
@Test
void deniesGroupWithoutAuthorization() {
SysGroup group = group(StatusEnum.ENABLED.getCode(), 0, future(), 2);
GroupAuthorizationPolicy.Result result = GroupAuthorizationPolicy.evaluate(group, 0, now());
assertThat(result.allowed()).isFalse();
assertThat(result.reason()).isEqualTo(GroupAuthorizationPolicy.Reason.AUTH_DISABLED);
}
@Test
void deniesExpiredGroup() {
SysGroup group = group(StatusEnum.ENABLED.getCode(), 1, past(), 2);
GroupAuthorizationPolicy.Result result = GroupAuthorizationPolicy.evaluate(group, 0, now());
assertThat(result.allowed()).isFalse();
assertThat(result.reason()).isEqualTo(GroupAuthorizationPolicy.Reason.EXPIRED);
}
@Test
void deniesWhenClientQuotaReached() {
SysGroup group = group(StatusEnum.ENABLED.getCode(), 1, future(), 2);
GroupAuthorizationPolicy.Result result = GroupAuthorizationPolicy.evaluate(group, 2, now());
assertThat(result.allowed()).isFalse();
assertThat(result.reason()).isEqualTo(GroupAuthorizationPolicy.Reason.CLIENT_QUOTA_REACHED);
}
private SysGroup group(Integer status, Integer authEnabled, Date expireTime, Integer maxClientCount) {
SysGroup group = new SysGroup();
group.setStatus(status);
group.setAuthEnabled(authEnabled);
group.setExpireTime(expireTime);
group.setMaxClientCount(maxClientCount);
return group;
}
private Date now() {
return new Date(1_700_000_000_000L);
}
private Date future() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(now());
calendar.add(Calendar.DATE, 1);
return calendar.getTime();
}
private Date past() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(now());
calendar.add(Calendar.DATE, -1);
return calendar.getTime();
}
}

View File

@@ -43,6 +43,21 @@ public class SysGroup {
*/
private Integer maxUserCount;
/**
* 最大客户端数,-1 表示不限制
*/
private Integer maxClientCount;
/**
* 授权开关1-已授权0-未授权
*/
private Integer authEnabled;
/**
* 授权到期时间,空表示不过期
*/
private Date expireTime;
/**
* 来源1-系统内置2-用户自定义
* @see SourceEnum

View File

@@ -120,6 +120,8 @@ public enum BizExceptionCodeEnum implements IBizExceptionCodeEnum {
systemRoleNotFoundWithRoleId("3122", "角色不存在,id=%s", "Role does not exist, id=%s", "RBAC"),
systemGroupMaxUsersBelowBound("3123", "最大用户数不能小于已绑定用户数,当前已绑定%s人", "Max users cannot be below the number already bound; currently %s user(s) bound.", "RBAC"),
systemGroupUserLimitExceeded("3124", "组用户数量超出限制", "Group user count exceeds the limit.", "RBAC"),
systemGroupAuthorizationUnavailable("3186", "当前用户组未授权或授权已过期", "The current user group is not authorized or the authorization has expired.", "RBAC"),
systemGroupClientLimitExceeded("3187", "用户组客户端数量超出限制", "Group client count exceeds the limit.", "RBAC"),
systemGroupNotFoundWithRowId("3125", "用户组不存在: id=%s", "User group does not exist: id=%s", "RBAC"),
systemGroupNotFoundWithGroupId("3126", "组不存在,id=%s", "Group does not exist, id=%s", "RBAC"),
systemGroupNotFoundWithGroupIdAlt("3127", "用户组不存在,id=%s", "User group does not exist, id=%s", "RBAC"),

View File

@@ -9,6 +9,7 @@ import com.xspaceagi.system.application.dto.permission.*;
import com.xspaceagi.system.application.service.SysDataPermissionApplicationService;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
import com.xspaceagi.system.application.service.SysSubjectPermissionApplicationService;
import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.domain.model.GroupBindMenuModel;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.domain.model.SortIndex;
@@ -16,6 +17,7 @@ import com.xspaceagi.system.infra.dao.entity.SysDataPermission;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.User;
import com.xspaceagi.system.spec.annotation.RequireResource;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.PageQueryVo;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.*;
@@ -36,6 +38,7 @@ import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
@@ -89,6 +92,7 @@ public class SysGroupController extends BaseController {
SysGroup group = new SysGroup();
BeanUtils.copyProperties(dto, group);
checkGroupManageScope(group.getId());
group.setCode(null); // code不允许更新
sysGroupApplicationService.updateGroup(group, getUser());
return ReqResult.success();
@@ -101,6 +105,7 @@ public class SysGroupController extends BaseController {
if (groupId == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(groupId);
sysGroupApplicationService.deleteGroup(groupId, getUser());
return ReqResult.success();
}
@@ -112,6 +117,7 @@ public class SysGroupController extends BaseController {
if (groupId == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(groupId);
SysGroup group = sysGroupApplicationService.getGroupById(groupId);
if (group == null) {
@@ -141,6 +147,7 @@ public class SysGroupController extends BaseController {
if (group == null) {
return ReqResult.error("用户组不存在");
}
checkGroupManageScope(group.getId());
SysGroupDto groupDto = new SysGroupDto();
BeanUtils.copyProperties(group, groupDto);
@@ -162,6 +169,7 @@ public class SysGroupController extends BaseController {
}
List<SortIndex> sortIndexList = dto.getItems().stream()
.map(item -> {
checkGroupManageScope(item.getId());
SortIndex model = new SortIndex();
model.setId(item.getId());
model.setParentId(item.getParentId());
@@ -183,6 +191,7 @@ public class SysGroupController extends BaseController {
}
List<SysGroup> groupList = sysGroupApplicationService.getGroupList(sysGroup);
groupList = filterGroupManageScope(groupList);
if (CollectionUtils.isEmpty(groupList)) {
return ReqResult.success();
}
@@ -214,6 +223,7 @@ public class SysGroupController extends BaseController {
}
SysGroupUserQueryDto queryDto = pageQueryVo.getQueryFilter();
Long groupId = queryDto.getGroupId();
checkGroupManageScope(groupId);
String userName = queryDto.getUserName();
long pageNo = pageQueryVo.getPageNo() != null ? pageQueryVo.getPageNo() : 1L;
long pageSize = pageQueryVo.getPageSize() != null ? pageQueryVo.getPageSize() : 10L;
@@ -238,6 +248,7 @@ public class SysGroupController extends BaseController {
if (groupId == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(groupId);
SysDataPermission dataPermission = sysDataPermissionApplicationService.getByTarget(PermissionTargetTypeEnum.GROUP, groupId);
SysDataPermissionBindDto result = SysDataPermissionConverter.toDto(dataPermission);
result = result == null ? new SysDataPermissionBindDto() : result;
@@ -282,6 +293,7 @@ public class SysGroupController extends BaseController {
if (dto == null || dto.getGroupId() == null) {
return ReqResult.error("用户组ID不能为空");
}
checkGroupManageScope(dto.getGroupId());
SysDataPermissionBindDto bindDto = dto.getDataPermission();
SysDataPermission dataPermission = SysDataPermissionConverter.toEntity(bindDto);
sysGroupApplicationService.bindDataPermission(dto.getGroupId(), dataPermission, getUser());
@@ -295,6 +307,7 @@ public class SysGroupController extends BaseController {
if (dto == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(dto.getGroupId());
sysGroupApplicationService.groupBindUser(dto.getGroupId(), dto.getUserIds(), getUser());
return ReqResult.success();
}
@@ -306,6 +319,7 @@ public class SysGroupController extends BaseController {
if (dto == null || dto.getGroupId() == null || dto.getUserId() == null) {
return ReqResult.error("用户组ID和用户ID不能为空");
}
checkGroupManageScope(dto.getGroupId());
sysGroupApplicationService.groupAddUser(dto.getGroupId(), dto.getUserId(), getUser());
return ReqResult.success();
}
@@ -317,6 +331,7 @@ public class SysGroupController extends BaseController {
if (dto == null || dto.getGroupId() == null || dto.getUserId() == null) {
return ReqResult.error("用户组ID和用户ID不能为空");
}
checkGroupManageScope(dto.getGroupId());
sysGroupApplicationService.groupRemoveUser(dto.getGroupId(), dto.getUserId(), getUser());
return ReqResult.success();
}
@@ -328,6 +343,7 @@ public class SysGroupController extends BaseController {
if (dto == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(dto.getGroupId());
// 用户组不允许绑定 生态市场/系统管理
checkForbiddenMenuCodes(dto.getMenuTree());
@@ -343,6 +359,7 @@ public class SysGroupController extends BaseController {
if (groupId == null) {
return ReqResult.error("参数不能为空");
}
checkGroupManageScope(groupId);
// 获取处理好的菜单树(已包含资源详细信息)
List<MenuNode> menuNodeList = sysGroupApplicationService.getMenuTreeByGroupId(groupId);
@@ -373,4 +390,41 @@ public class SysGroupController extends BaseController {
}
}
}
}
private List<SysGroup> filterGroupManageScope(List<SysGroup> groupList) {
if (isPlatformAdmin() || CollectionUtils.isEmpty(groupList)) {
return groupList;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
return groupList.stream()
.filter(group -> group.getId() != null && allowedGroupIds.contains(group.getId()))
.collect(Collectors.toList());
}
private void checkGroupManageScope(Long groupId) {
if (isPlatformAdmin()) {
return;
}
if (groupId == null || !currentUserGroupIds().contains(groupId)) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
}
private Set<Long> currentUserGroupIds() {
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
.map(SysGroup::getId)
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
return getCurrentUserDto().getRole() == User.Role.Admin;
}
private UserDto getCurrentUserDto() {
UserDto userDto = (UserDto) RequestContext.get().getUser();
if (userDto == null) {
throw BizException.of(ErrorCodeEnum.UNAUTHORIZED, BizExceptionCodeEnum.systemUserNotLoggedInWeb);
}
return userDto;
}
}

View File

@@ -1,5 +1,6 @@
package com.xspaceagi.system.web.controller.permission;
import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.application.dto.permission.*;
import com.xspaceagi.system.application.service.SysDataPermissionApplicationService;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
@@ -7,10 +8,15 @@ import com.xspaceagi.system.application.service.SysRoleApplicationService;
import com.xspaceagi.system.application.service.impl.SysUserPermissionCacheServiceImpl;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.SysRole;
import com.xspaceagi.system.infra.dao.entity.User;
import com.xspaceagi.system.sdk.service.dto.UserDataPermissionDto;
import com.xspaceagi.system.spec.annotation.RequireResource;
import com.xspaceagi.system.spec.annotation.SaasAdmin;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.spec.utils.I18nUtil;
import com.xspaceagi.system.web.controller.base.BaseController;
import io.swagger.v3.oas.annotations.Operation;
@@ -22,6 +28,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.xspaceagi.system.spec.enums.ResourceEnum.*;
@@ -48,6 +55,7 @@ public class SysUserController extends BaseController {
if (userId == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(userId);
List<SysRole> roleList = sysRoleApplicationService.getRoleListByUserId(userId);
if (CollectionUtils.isEmpty(roleList)) {
@@ -70,6 +78,8 @@ public class SysUserController extends BaseController {
if (dto == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(dto.getUserId());
checkRoleBindScope(dto.getRoleIds());
sysRoleApplicationService.userBindRole(dto.getUserId(), dto.getRoleIds(), getUser());
return ReqResult.success();
}
@@ -81,6 +91,7 @@ public class SysUserController extends BaseController {
if (userId == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(userId);
List<SysGroup> groupList = sysGroupApplicationService.getGroupListByUserId(userId);
if (CollectionUtils.isEmpty(groupList)) {
return ReqResult.success();
@@ -102,6 +113,8 @@ public class SysUserController extends BaseController {
if (dto == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(dto.getUserId());
checkGroupBindScope(dto.getGroupIds());
sysGroupApplicationService.userBindGroup(dto.getUserId(), dto.getGroupIds(), getUser());
return ReqResult.success();
}
@@ -113,6 +126,7 @@ public class SysUserController extends BaseController {
if (userId == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(userId);
List<MenuNodeDto> menuTree = sysUserPermissionCacheService.getUserMenuTree(userId);
I18nUtil.replaceSystemMessage(menuTree);
@@ -126,10 +140,66 @@ public class SysUserController extends BaseController {
if (userId == null) {
return ReqResult.error("参数不能为空");
}
checkUserManageScope(userId);
UserDataPermissionDto dataPermission = sysDataPermissionApplicationService.getUserDataPermission(userId);
return ReqResult.success(dataPermission);
}
private void checkUserManageScope(Long userId) {
if (isPlatformAdmin()) {
return;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
boolean inScope = sysGroupApplicationService.getEffectiveGroupListByUserId(userId).stream()
.map(SysGroup::getId)
.anyMatch(allowedGroupIds::contains);
if (!inScope) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
}
private void checkGroupBindScope(List<Long> groupIds) {
if (isPlatformAdmin() || CollectionUtils.isEmpty(groupIds)) {
return;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
boolean allInScope = groupIds.stream().allMatch(allowedGroupIds::contains);
if (!allInScope) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
}
private void checkRoleBindScope(List<Long> roleIds) {
if (isPlatformAdmin() || CollectionUtils.isEmpty(roleIds)) {
return;
}
Set<Long> allowedRoleIds = sysRoleApplicationService.getRoleListByUserId(getCurrentUserDto().getId()).stream()
.map(SysRole::getId)
.collect(Collectors.toSet());
boolean allInScope = roleIds.stream().allMatch(allowedRoleIds::contains);
if (!allInScope) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
}
private Set<Long> currentUserGroupIds() {
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
.map(SysGroup::getId)
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
return getCurrentUserDto().getRole() == User.Role.Admin;
}
private UserDto getCurrentUserDto() {
UserDto userDto = (UserDto) RequestContext.get().getUser();
if (userDto == null) {
throw BizException.of(ErrorCodeEnum.UNAUTHORIZED, BizExceptionCodeEnum.systemUserNotLoggedInWeb);
}
return userDto;
}
@SaasAdmin
@Operation(summary = "清除指定用户权限缓存")
@GetMapping("/cache/clear-user")
@@ -148,4 +218,4 @@ public class SysUserController extends BaseController {
sysUserPermissionCacheService.clearCacheAllByTenant(tenantId);
return ReqResult.success();
}
}
}