去除 LoginUser 的 roleIds、deptId 字段,简化

This commit is contained in:
YunaiV 2022-05-07 00:39:39 +08:00
parent d8a242629b
commit 8737674d74
16 changed files with 179 additions and 189 deletions

View File

@ -22,7 +22,6 @@
<swagger-annotations.version>1.5.22</swagger-annotations.version> <swagger-annotations.version>1.5.22</swagger-annotations.version>
<servlet.versoin>2.5</servlet.versoin> <servlet.versoin>2.5</servlet.versoin>
<!-- DB 相关 --> <!-- DB 相关 -->
<mysql.version>5.1.46</mysql.version>
<druid.version>1.2.8</druid.version> <druid.version>1.2.8</druid.version>
<mybatis-plus.version>3.4.3.4</mybatis-plus.version> <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
<mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version> <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version>
@ -76,12 +75,6 @@
<version>${spring.boot.version}</version> <version>${spring.boot.version}</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
<exclusions>
<exclusion>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- 业务组件 --> <!-- 业务组件 -->

View File

@ -54,6 +54,13 @@ public class CollectionUtils {
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
} }
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
if (CollUtil.isEmpty(from)) { if (CollUtil.isEmpty(from)) {
return new HashSet<>(); return new HashSet<>();

View File

@ -1,11 +1,13 @@
package cn.iocoder.yudao.framework.datapermission.core.dept.rule; package cn.iocoder.yudao.framework.datapermission.core.dept.rule;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule; import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
@ -13,7 +15,6 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
@ -24,10 +25,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression; import net.sf.jsqlparser.expression.operators.relational.InExpression;
import java.util.HashMap; import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/** /**
* 基于部门的 {@link DataPermissionRule} 数据权限规则实现 * 基于部门的 {@link DataPermissionRule} 数据权限规则实现
@ -50,6 +48,11 @@ import java.util.Set;
@Slf4j @Slf4j
public class DeptDataPermissionRule implements DataPermissionRule { public class DeptDataPermissionRule implements DataPermissionRule {
/**
* LoginUser Context 缓存 Key
*/
protected static final String CONTEXT_KEY = DeptDataPermissionRule.class.getSimpleName();
private static final String DEPT_COLUMN_NAME = "dept_id"; private static final String DEPT_COLUMN_NAME = "dept_id";
private static final String USER_COLUMN_NAME = "user_id"; private static final String USER_COLUMN_NAME = "user_id";
@ -90,13 +93,23 @@ public class DeptDataPermissionRule implements DataPermissionRule {
if (loginUser == null) { if (loginUser == null) {
return null; return null;
} }
// 只有管理员类型的用户才进行数据权限的处理
if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {
return null;
}
// 获得数据权限 // 获得数据权限
DeptDataPermissionRespDTO deptDataPermission = deptDataPermissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
// 从上下文中拿不到则调用逻辑进行获取
if (deptDataPermission == null) { if (deptDataPermission == null) {
log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser)); deptDataPermission = deptDataPermissionService.getDeptDataPermission(loginUser.getId());
throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限", if (deptDataPermission == null) {
loginUser.getId(), tableName, tableAlias.getName())); log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",
loginUser.getId(), tableName, tableAlias.getName()));
}
// 添加到上下文中避免重复计算
loginUser.setContext(CONTEXT_KEY, deptDataPermission);
} }
// 情况一如果是 ALL 可查看全部则无需拼接条件 // 情况一如果是 ALL 可查看全部则无需拼接条件
@ -111,8 +124,8 @@ public class DeptDataPermissionRule implements DataPermissionRule {
} }
// 情况三拼接 Dept User 的条件最后组合 // 情况三拼接 Dept User 的条件最后组合
Expression deptExpression = this.buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds()); Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());
Expression userExpression = this.buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId()); Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());
if (deptExpression == null && userExpression == null) { if (deptExpression == null && userExpression == null) {
// TODO 芋艿获得不到条件的时候暂时不抛出异常而是不返回数据 // TODO 芋艿获得不到条件的时候暂时不抛出异常而是不返回数据
log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]", log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.framework.datapermission.core.dept.service; package cn.iocoder.yudao.framework.datapermission.core.dept.service;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
/** /**
* 基于部门的数据权限 Framework Service 接口 * 基于部门的数据权限 Framework Service 接口
@ -14,9 +13,9 @@ public interface DeptDataPermissionFrameworkService {
/** /**
* 获得登陆用户的部门数据权限 * 获得登陆用户的部门数据权限
* *
* @param loginUser 登陆用户 * @param userId 用户编号
* @return 部门数据权限 * @return 部门数据权限
*/ */
DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser); DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
} }

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.datapermission.core.dept.rule;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
@ -69,7 +70,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法 // mock 方法
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// 调用 // 调用
@ -88,16 +90,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true); DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true);
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 调用 // 调用
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertNull(expression); assertNull(expression);
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }
@ -109,16 +113,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO(); DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO();
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 调用 // 调用
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertEquals("null = null", expression.toString()); assertEquals("null = null", expression.toString());
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }
@ -130,17 +136,19 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
.setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true); .setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true);
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 调用 // 调用
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertSame(EXPRESSION_NULL, expression); assertSame(EXPRESSION_NULL, expression);
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }
@ -152,12 +160,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
.setSelf(true); .setSelf(true);
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 添加 user 字段配置 // 添加 user 字段配置
rule.addUserColumn("t_user", "id"); rule.addUserColumn("t_user", "id");
@ -165,6 +174,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertEquals("u.id = 1", expression.toString()); assertEquals("u.id = 1", expression.toString());
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }
@ -176,12 +186,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
.setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)); .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L));
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 添加 dept 字段配置 // 添加 dept 字段配置
rule.addDeptColumn("t_user", "dept_id"); rule.addDeptColumn("t_user", "dept_id");
@ -189,6 +200,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertEquals("u.dept_id IN (10, 20)", expression.toString()); assertEquals("u.dept_id IN (10, 20)", expression.toString());
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }
@ -200,12 +212,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
String tableName = "t_user"; String tableName = "t_user";
Alias tableAlias = new Alias("u"); Alias tableAlias = new Alias("u");
// mock 方法LoginUser // mock 方法LoginUser
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser);
// mock 方法DeptDataPermissionRespDTO // mock 方法DeptDataPermissionRespDTO
DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO()
.setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true); .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true);
when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission);
// 添加 user 字段配置 // 添加 user 字段配置
rule.addUserColumn("t_user", "id"); rule.addUserColumn("t_user", "id");
// 添加 dept 字段配置 // 添加 dept 字段配置
@ -215,6 +228,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest {
Expression expression = rule.getExpression(tableName, tableAlias); Expression expression = rule.getExpression(tableName, tableAlias);
// 断言 // 断言
assertEquals("u.dept_id IN (10, 20) OR u.id = 1", expression.toString()); assertEquals("u.dept_id IN (10, 20) OR u.id = 1", expression.toString());
assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }

View File

@ -50,17 +50,6 @@ public class LoginUser implements UserDetails {
*/ */
private Long tenantId; private Long tenantId;
// ========== UserTypeEnum.ADMIN 独有字段 ==========
// TODO 芋艿可以通过定义一个 Map<String, String> exts 的方式去除管理员的字段不过这样会导致系统比较复杂所以暂时不去掉先
/**
* 角色编号数组
*/
private Set<Long> roleIds;
/**
* 部门编号
*/
private Long deptId;
// ========== 上下文 ========== // ========== 上下文 ==========
/** /**
* 上下文字段不进行持久化 * 上下文字段不进行持久化

View File

@ -11,7 +11,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.Set;
/** /**
* 安全服务工具类 * 安全服务工具类
@ -79,17 +78,6 @@ public class SecurityFrameworkUtils {
return loginUser != null ? loginUser.getId() : null; return loginUser != null ? loginUser.getId() : null;
} }
/**
* 获得当前用户的角色编号数组
*
* @return 角色编号数组
*/
@Nullable
public static Set<Long> getLoginUserRoleIds() {
LoginUser loginUser = getLoginUser();
return loginUser != null ? loginUser.getRoleIds() : null;
}
/** /**
* 设置当前用户 * 设置当前用户
* *

View File

@ -84,7 +84,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Override @Override
public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) { public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) {
// 使用手机 + 密码进行登录 // 使用手机 + 密码进行登录
LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword());
// 缓存登录用户到 Redis 返回 Token 令牌 // 缓存登录用户到 Redis 返回 Token 令牌
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);

View File

@ -26,12 +26,13 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserRoleIds; import static java.util.Collections.singleton;
@Api(tags = "管理后台 - 认证") @Api(tags = "管理后台 - 认证")
@RestController @RestController
@ -69,12 +70,12 @@ public class AuthController {
return null; return null;
} }
// 获得角色列表 // 获得角色列表
List<RoleDO> roleList = roleService.getRolesFromCache(getLoginUserRoleIds()); Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
List<RoleDO> roleList = roleService.getRolesFromCache(roleIds);
// 获得菜单列表 // 获得菜单列表
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache( List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
getLoginUserRoleIds(), // 注意基于登录的角色因为后续的权限判断也是基于它
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()),
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
// 拼接结果返回 // 拼接结果返回
return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); return success(AuthConvert.INSTANCE.convert(user, roleList, menuList));
} }
@ -82,11 +83,12 @@ public class AuthController {
@GetMapping("/list-menus") @GetMapping("/list-menus")
@ApiOperation("获得登录用户的菜单列表") @ApiOperation("获得登录用户的菜单列表")
public CommonResult<List<AuthMenuRespVO>> getMenus() { public CommonResult<List<AuthMenuRespVO>> getMenus() {
// 获得角色列表
Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
// 获得用户拥有的菜单列表 // 获得用户拥有的菜单列表
List<MenuDO> menuList = permissionService.getRoleMenuListFromCache( List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds,
getLoginUserRoleIds(), // 注意基于登录的角色因为后续的权限判断也是基于它
SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型 SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型
SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的
// 转换成 Tree 结构返回 // 转换成 Tree 结构返回
return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); return success(AuthConvert.INSTANCE.buildMenuTree(menuList));
} }

View File

@ -1,15 +1,16 @@
package cn.iocoder.yudao.module.system.controller.admin.auth; package cn.iocoder.yudao.module.system.controller.admin.auth;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert; import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -49,7 +50,8 @@ public class UserSessionController {
// 获得拼接需要的数据 // 获得拼接需要的数据
Map<Long, AdminUserDO> userMap = userService.getUserMap( Map<Long, AdminUserDO> userMap = userService.getUserMap(
convertList(pageResult.getList(), UserSessionDO::getUserId)); convertList(pageResult.getList(), UserSessionDO::getUserId,
session -> session.getUserType().equals(UserTypeEnum.ADMIN.getValue())));
Map<Long, DeptDO> deptMap = deptService.getDeptMap( Map<Long, DeptDO> deptMap = deptService.getDeptMap(
convertList(userMap.values(), AdminUserDO::getDeptId)); convertList(userMap.values(), AdminUserDO::getDeptId));
// 拼接结果返回 // 拼接结果返回

View File

@ -17,7 +17,6 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import cn.iocoder.yudao.module.system.service.common.CaptchaService; import cn.iocoder.yudao.module.system.service.common.CaptchaService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService; import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -36,12 +35,10 @@ import org.springframework.util.Assert;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Validator; import javax.validation.Validator;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;
/** /**
* Auth Service 实现类 * Auth Service 实现类
@ -60,8 +57,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // UserService 存在重名 @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // UserService 存在重名
private AdminUserService userService; private AdminUserService userService;
@Resource @Resource
private PermissionService permissionService;
@Resource
private CaptchaService captchaService; private CaptchaService captchaService;
@Resource @Resource
private LoginLogService loginLogService; private LoginLogService loginLogService;
@ -211,16 +206,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
} }
} }
/**
* 获得 User 拥有的角色编号数组
*
* @param userId 用户编号
* @return 角色编号数组
*/
private Set<Long> getUserRoleIds(Long userId) {
return permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
}
@Override @Override
public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
// 使用 code 授权码进行登录然后获得到绑定的用户编号 // 使用 code 授权码进行登录然后获得到绑定的用户编号
@ -318,17 +303,13 @@ public class AdminAuthServiceImpl implements AdminAuthService {
} }
// 刷新 LoginUser 缓存 // 刷新 LoginUser 缓存
LoginUser newLoginUser= this.buildLoginUser(user); LoginUser newLoginUser= buildLoginUser(user);
userSessionService.refreshUserSession(token, newLoginUser); userSessionService.refreshUserSession(token, newLoginUser);
return newLoginUser; return newLoginUser;
} }
private LoginUser buildLoginUser(AdminUserDO user) { private LoginUser buildLoginUser(AdminUserDO user) {
LoginUser loginUser = AuthConvert.INSTANCE.convert(user); return AuthConvert.INSTANCE.convert(user);
// 补全字段
loginUser.setDeptId(user.getDeptId());
loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId()));
return loginUser;
} }
} }

View File

@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
@ -22,6 +22,8 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -36,6 +38,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.*;
import java.util.function.Supplier;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static java.util.Collections.singleton;
/** /**
* 权限 Service 实现类 * 权限 Service 实现类
@ -46,11 +52,6 @@ import java.util.*;
@Slf4j @Slf4j
public class PermissionServiceImpl implements PermissionService { public class PermissionServiceImpl implements PermissionService {
/**
* LoginUser Context 缓存 Key
*/
public static final String CONTEXT_KEY = PermissionServiceImpl.class.getSimpleName();
/** /**
* 定时执行 {@link #schedulePeriodicRefresh()} 的周期 * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
* 因为已经通过 Redis Pub/Sub 机制所以频率不需要高 * 因为已经通过 Redis Pub/Sub 机制所以频率不需要高
@ -93,6 +94,8 @@ public class PermissionServiceImpl implements PermissionService {
private MenuService menuService; private MenuService menuService;
@Resource @Resource
private DeptService deptService; private DeptService deptService;
@Resource
private AdminUserService userService;
@Resource @Resource
private PermissionProducer permissionProducer; private PermissionProducer permissionProducer;
@ -319,7 +322,7 @@ public class PermissionServiceImpl implements PermissionService {
} }
// 获得当前登录的角色如果为空说明没有权限 // 获得当前登录的角色如果为空说明没有权限
Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
if (CollUtil.isEmpty(roleIds)) { if (CollUtil.isEmpty(roleIds)) {
return false; return false;
} }
@ -354,7 +357,7 @@ public class PermissionServiceImpl implements PermissionService {
} }
// 获得当前登录的角色如果为空说明没有权限 // 获得当前登录的角色如果为空说明没有权限
Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus()));
if (CollUtil.isEmpty(roleIds)) { if (CollUtil.isEmpty(roleIds)) {
return false; return false;
} }
@ -368,16 +371,18 @@ public class PermissionServiceImpl implements PermissionService {
} }
@Override @Override
public DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser) { @DataPermission(enable = false) // 关闭数据权限不然就会出现递归获取数据权限的问题
// 判断是否 context 已经缓存 public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) {
DeptDataPermissionRespDTO result = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class); DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO();
if (result != null) { // 获得用户的角色
Set<Long> roleIds = getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
if (CollUtil.isEmpty(roleIds)) {
return result; return result;
} }
List<RoleDO> roles = roleService.getRolesFromCache(roleIds);
// 创建 DeptDataPermissionRespDTO 对象 // 获得用户的部门编号的缓存通过 Guava Suppliers 惰性求值即有且仅有第一次发起 DB 的查询
result = new DeptDataPermissionRespDTO(); Supplier<Long> userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
List<RoleDO> roles = roleService.getRolesFromCache(loginUser.getRoleIds()); // 遍历每个角色计算
for (RoleDO role : roles) { for (RoleDO role : roles) {
// 为空时跳过 // 为空时跳过
if (role.getDataScope() == null) { if (role.getDataScope() == null) {
@ -393,20 +398,20 @@ public class PermissionServiceImpl implements PermissionService {
CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds());
// 自定义可见部门时保证可以看到自己所在的部门否则一些场景下可能会有问题 // 自定义可见部门时保证可以看到自己所在的部门否则一些场景下可能会有问题
// 例如说登录时基于 t_user username 查询会可能被 dept_id 过滤掉 // 例如说登录时基于 t_user username 查询会可能被 dept_id 过滤掉
CollUtil.addAll(result.getDeptIds(), loginUser.getDeptId()); CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get());
continue; continue;
} }
// 情况三DEPT_ONLY // 情况三DEPT_ONLY
if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) {
CollectionUtils.addIfNotNull(result.getDeptIds(), loginUser.getDeptId()); CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptIdCache.get());
continue; continue;
} }
// 情况四DEPT_DEPT_AND_CHILD // 情况四DEPT_DEPT_AND_CHILD
if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) {
List<DeptDO> depts = deptService.getDeptsByParentIdFromCache(loginUser.getDeptId(), true); List<DeptDO> depts = deptService.getDeptsByParentIdFromCache(userDeptIdCache.get(), true);
CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId));
//添加本身部门id // 添加本身部门编号
CollUtil.addAll(result.getDeptIds(), loginUser.getDeptId()); CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get());
continue; continue;
} }
// 情况五SELF // 情况五SELF
@ -415,11 +420,8 @@ public class PermissionServiceImpl implements PermissionService {
continue; continue;
} }
// 未知情况error log 即可 // 未知情况error log 即可
log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", loginUser.getId(), JsonUtils.toJsonString(result)); log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result));
} }
// 添加到缓存并返回
loginUser.setContext(CONTEXT_KEY, result);
return result; return result;
} }

View File

@ -81,6 +81,7 @@ public class TenantServiceImpl implements TenantService {
@Getter @Getter
private volatile Date maxUpdateTime; private volatile Date maxUpdateTime;
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
@Autowired(required = false) // 由于 yudao.tenant.enable 配置项可以关闭多租户的功能所以这里只能不强制注入 @Autowired(required = false) // 由于 yudao.tenant.enable 配置项可以关闭多租户的功能所以这里只能不强制注入
private TenantProperties tenantProperties; private TenantProperties tenantProperties;

View File

@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -61,6 +62,7 @@ public class AdminUserServiceImpl implements AdminUserService {
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Resource @Resource
@Lazy // 延迟避免循环依赖报错
private TenantService tenantService; private TenantService tenantService;
@Resource @Resource

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.system.service.auth; package cn.iocoder.yudao.module.system.service.auth;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.framework.test.core.util.AssertUtils; import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
@ -12,7 +11,6 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.service.common.CaptchaService; import cn.iocoder.yudao.module.system.service.common.CaptchaService;
import cn.iocoder.yudao.module.system.service.dept.PostService; import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.social.SocialUserService; import cn.iocoder.yudao.module.system.service.social.SocialUserService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -34,7 +32,6 @@ import java.util.Set;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -49,8 +46,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
@MockBean @MockBean
private AdminUserService userService; private AdminUserService userService;
@MockBean @MockBean
private PermissionService permissionService;
@MockBean
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@MockBean @MockBean
private Authentication authentication; private Authentication authentication;
@ -108,16 +103,11 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
// mock 方法 01 // mock 方法 01
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
when(userService.getUser(eq(userId))).thenReturn(user); when(userService.getUser(eq(userId))).thenReturn(user);
// mock 方法 02
Set<Long> roleIds = randomSet(Long.class);
when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus()))))
.thenReturn(roleIds);
// 调用 // 调用
LoginUser loginUser = authService.mockLogin(userId); LoginUser loginUser = authService.mockLogin(userId);
// 断言 // 断言
AssertUtils.assertPojoEquals(user, loginUser, "updateTime"); AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
assertEquals(roleIds, loginUser.getRoleIds());
} }
@Test @Test
@ -247,15 +237,10 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
// mock authentication // mock authentication
Long userId = randomLongId(); Long userId = randomLongId();
Set<Long> userRoleIds = randomSet(Long.class); Set<Long> userRoleIds = randomSet(Long.class);
LoginUser loginUser = randomPojo(LoginUser.class, o -> { LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(userId));
o.setId(userId);
o.setRoleIds(userRoleIds);
});
when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword())))
.thenReturn(authentication); .thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(loginUser); when(authentication.getPrincipal()).thenReturn(loginUser);
// mock 获得 User 拥有的角色编号数组
when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds);
// mock 缓存登录用户到 Redis // mock 缓存登录用户到 Redis
String token = randomString(); String token = randomString();
when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(token); when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(token);

View File

@ -1,20 +1,22 @@
package cn.iocoder.yudao.module.system.service.permission; package cn.iocoder.yudao.module.system.service.permission;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -25,10 +27,10 @@ import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -54,6 +56,9 @@ public class PermissionServiceTest extends BaseDbUnitTest {
private MenuService menuService; private MenuService menuService;
@MockBean @MockBean
private DeptService deptService; private DeptService deptService;
@MockBean
private AdminUserService userService;
@MockBean @MockBean
private PermissionProducer permissionProducer; private PermissionProducer permissionProducer;
@ -124,112 +129,119 @@ public class PermissionServiceTest extends BaseDbUnitTest {
assertPojoEquals(dbUserRoles.get(0), userRoleDO02); assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
} }
@Test // 测试从 context 获取的场景
public void testGetDeptDataPermission_fromContext() {
// 准备参数
LoginUser loginUser = randomPojo(LoginUser.class);
// mock 方法
DeptDataPermissionRespDTO respDTO = new DeptDataPermissionRespDTO();
loginUser.setContext(PermissionServiceImpl.CONTEXT_KEY, respDTO);
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser);
// 断言
assertSame(respDTO, result);
}
@Test @Test
public void testGetDeptDataPermission_All() { public void testGetDeptDataPermission_All() {
// 准备参数 // 准备参数
LoginUser loginUser = randomPojo(LoginUser.class); Long userId = 1L;
// mock 方法 // mock 用户的角色编号
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())); userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); // mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));
when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);
// 调用 // 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言 // 断言
assertTrue(result.getAll()); assertTrue(result.getAll());
assertFalse(result.getSelf()); assertFalse(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds())); assertTrue(CollUtil.isEmpty(result.getDeptIds()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
@Test @Test
public void testGetDeptDataPermission_DeptCustom() { public void testGetDeptDataPermission_DeptCustom() {
// 准备参数 // 准备参数
LoginUser loginUser = randomPojo(LoginUser.class); Long userId = 1L;
// mock 方法 // mock 用户的角色编号
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())); userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); // mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));
when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的看看会不会重复调用
// 调用 // 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L);
// 断言 // 断言
assertFalse(result.getAll()); assertFalse(result.getAll());
assertFalse(result.getSelf()); assertFalse(result.getSelf());
assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size());
assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds()));
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
@Test @Test
public void testGetDeptDataPermission_DeptOnly() { public void testGetDeptDataPermission_DeptOnly() {
// 准备参数 // 准备参数
LoginUser loginUser = randomPojo(LoginUser.class); Long userId = 1L;
// mock 方法 // mock 用户的角色编号
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())); userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); // mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));
when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的看看会不会重复调用
// 调用 // 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L);
// 断言 // 断言
assertFalse(result.getAll()); assertFalse(result.getAll());
assertFalse(result.getSelf()); assertFalse(result.getSelf());
assertEquals(1, result.getDeptIds().size()); assertEquals(1, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
@Test @Test
public void testGetDeptDataPermission_DeptAndChild() { public void testGetDeptDataPermission_DeptAndChild() {
// 准备参数 // 准备参数
LoginUser loginUser = randomPojo(LoginUser.class); Long userId = 1L;
// mock 方法角色 // mock 用户的角色编号
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())); userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); // mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));
when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的看看会不会重复调用
// mock 方法部门 // mock 方法部门
DeptDO deptDO = randomPojo(DeptDO.class); DeptDO deptDO = randomPojo(DeptDO.class);
when(deptService.getDeptsByParentIdFromCache(eq(loginUser.getDeptId()), eq(true))) when(deptService.getDeptsByParentIdFromCache(eq(3L), eq(true)))
.thenReturn(singletonList(deptDO)); .thenReturn(singletonList(deptDO));
// 调用 // 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言 // 断言
assertFalse(result.getAll()); assertFalse(result.getAll());
assertFalse(result.getSelf()); assertFalse(result.getSelf());
assertEquals(2, result.getDeptIds().size()); assertEquals(2, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId()));
assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
@Test @Test
public void testGetDeptDataPermission_Self() { public void testGetDeptDataPermission_Self() {
// 准备参数 // 准备参数
LoginUser loginUser = randomPojo(LoginUser.class); Long userId = 1L;
// mock 方法 // mock 用户的角色编号
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())); userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L));
when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); // mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO));
when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO);
// 调用 // 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言 // 断言
assertFalse(result.getAll()); assertFalse(result.getAll());
assertTrue(result.getSelf()); assertTrue(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds())); assertTrue(CollUtil.isEmpty(result.getDeptIds()));
assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class));
} }
} }