feat: 可替换实现UserDetailsConverter,支持个性化业务定制

This commit is contained in:
wangyu 2024-11-28 15:47:44 +08:00
parent ffd026c421
commit 39fe3ace5d
6 changed files with 115 additions and 95 deletions

View File

@ -20,6 +20,7 @@ import dev.flyfish.framework.user.config.converter.EncryptedAuthenticationConver
import dev.flyfish.framework.user.config.properties.SecurityProperties; import dev.flyfish.framework.user.config.properties.SecurityProperties;
import dev.flyfish.framework.user.domain.UserQo; import dev.flyfish.framework.user.domain.UserQo;
import dev.flyfish.framework.user.initializer.UserInitializer; import dev.flyfish.framework.user.initializer.UserInitializer;
import dev.flyfish.framework.user.service.DefaultUserDetailsConverter;
import dev.flyfish.framework.user.service.DepartmentService; import dev.flyfish.framework.user.service.DepartmentService;
import dev.flyfish.framework.user.service.UserDetailsConverter; import dev.flyfish.framework.user.service.UserDetailsConverter;
import dev.flyfish.framework.user.service.UserService; import dev.flyfish.framework.user.service.UserService;
@ -231,6 +232,6 @@ public class WebSecurityConfig {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public UserDetailsConverter userDetailsConverter(DepartmentService departmentService) { public UserDetailsConverter userDetailsConverter(DepartmentService departmentService) {
return new UserDetailsConverter(departmentService); return new DefaultUserDetailsConverter(departmentService);
} }
} }

View File

@ -2,7 +2,6 @@ package dev.flyfish.framework.user.config.audit;
import dev.flyfish.framework.auditor.ReactiveBeanPoster; import dev.flyfish.framework.auditor.ReactiveBeanPoster;
import dev.flyfish.framework.domain.po.Role; import dev.flyfish.framework.domain.po.Role;
import dev.flyfish.framework.user.service.UserDetailsConverter;
import dev.flyfish.framework.utils.ReactiveRedisOperations; import dev.flyfish.framework.utils.ReactiveRedisOperations;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -18,7 +17,6 @@ import reactor.core.publisher.Mono;
public class RoleBeanPoster implements ReactiveBeanPoster<Role> { public class RoleBeanPoster implements ReactiveBeanPoster<Role> {
private final ReactiveRedisOperations reactiveRedisOperations; private final ReactiveRedisOperations reactiveRedisOperations;
private final UserDetailsConverter userDetailsConverter;
/** /**
* 对入库的实体进行审查并执行额外功能 * 对入库的实体进行审查并执行额外功能

View File

@ -2,11 +2,11 @@ package dev.flyfish.framework.user.config.audit;
import dev.flyfish.framework.auditor.ReactiveBeanAuditor; import dev.flyfish.framework.auditor.ReactiveBeanAuditor;
import dev.flyfish.framework.auditor.ReactiveBeanPoster; import dev.flyfish.framework.auditor.ReactiveBeanPoster;
import dev.flyfish.framework.user.service.UserDetailsConverter;
import dev.flyfish.framework.user.utils.StrengthUtils;
import dev.flyfish.framework.user.config.constants.UserCacheKeys;
import dev.flyfish.framework.domain.po.User; import dev.flyfish.framework.domain.po.User;
import dev.flyfish.framework.enums.UserType; import dev.flyfish.framework.enums.UserType;
import dev.flyfish.framework.user.config.constants.UserCacheKeys;
import dev.flyfish.framework.user.service.UserDetailsConverter;
import dev.flyfish.framework.user.utils.StrengthUtils;
import dev.flyfish.framework.utils.Assert; import dev.flyfish.framework.utils.Assert;
import dev.flyfish.framework.utils.ReactiveRedisOperations; import dev.flyfish.framework.utils.ReactiveRedisOperations;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@ -0,0 +1,101 @@
package dev.flyfish.framework.user.service;
import dev.flyfish.framework.domain.base.IUser;
import dev.flyfish.framework.domain.po.Department;
import dev.flyfish.framework.domain.po.Role;
import dev.flyfish.framework.enums.UserType;
import dev.flyfish.framework.user.domain.AdminUserDetails;
import dev.flyfish.framework.utils.CopyUtils;
import dev.flyfish.framework.utils.DepartUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetails;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@RequiredArgsConstructor
public class DefaultUserDetailsConverter implements UserDetailsConverter {
private final DepartmentService departmentService;
/**
* 将用户映射为用户详情查询所有权限code
*
* @param user 用户信息
* @return 结果
*/
public Mono<UserDetails> mapToUserDetails(IUser user) {
AdminUserDetails userDetail = new AdminUserDetails();
CopyUtils.copyProps(user, userDetail);
userDetail.setAuthorityCodes(Collections.singleton(Department.PUBLIC));
if (user.getType() == UserType.SUPER_ADMIN) {
userDetail.setAuthorityCodes(null);
} else if (CollectionUtils.isNotEmpty(user.getDepartments())) {
return judgeDeparts(user, userDetail);
}
return Mono.just(userDetail);
}
/**
* 判断部门包含关系默认看不到本部门的数据却可以看到子部门的
*
* @param user 用户信息
* @param userDetails 用户详情
*/
private Mono<UserDetails> judgeDeparts(IUser user, AdminUserDetails userDetails) {
// 整合数据权限
Set<Role.Authority> authorities = user.getRoles().stream()
.flatMap(role -> null == role.getAuthorities() ? Stream.empty() : role.getAuthorities().stream())
.collect(Collectors.toSet());
// 默认使用用户当前选择的权限进行查询
String authority = user.getAuthority();
// 根据权限组装查询条件
Set<String> departs = StringUtils.isNotBlank(authority) ? Collections.singleton(authority) :
DepartUtils.mergeDeparts(user.getDepartments());
// 查询所有子部门id
return departmentService.getSubCodes(departs)
.map(codes -> {
// 取出权限便于判定
boolean admin = authorities.contains(Role.Authority.ADMIN);
boolean view = authorities.contains(Role.Authority.VIEW);
boolean viewChildren = authorities.contains(Role.Authority.VIEW_CHILDREN);
// 全部
Set<String> all = new HashSet<>();
// 拥有查看本部门的权限
if (admin || view) {
all.addAll(departs);
}
// 拥有查看所有下级的权限
if (admin || viewChildren) {
all.addAll(codes);
}
// 最终设置权限
if (CollectionUtils.isNotEmpty(all)) {
userDetails.setAuthorityCodes(union(all, userDetails.getAuthorityCodes()));
}
userDetails.setVisibleDeparts(union(codes, departs));
return userDetails;
});
}
/**
* 链接两个set
*
* @param a 第一个
* @param b 第二个
* @return 结果
*/
private Set<String> union(Set<String> a, Set<String> b) {
Set<String> result = new HashSet<>();
result.addAll(a);
result.addAll(b);
return result;
}
}

View File

@ -1,13 +1,14 @@
package dev.flyfish.framework.user.service; package dev.flyfish.framework.user.service;
import dev.flyfish.framework.user.config.constants.UserCacheKeys;
import dev.flyfish.framework.configuration.jwt.TokenProvider; import dev.flyfish.framework.configuration.jwt.TokenProvider;
import dev.flyfish.framework.domain.base.IUser; import dev.flyfish.framework.domain.base.IUser;
import dev.flyfish.framework.enums.UserStatus; import dev.flyfish.framework.enums.UserStatus;
import dev.flyfish.framework.service.FlyfishUserDetailsService; import dev.flyfish.framework.service.FlyfishUserDetailsService;
import dev.flyfish.framework.user.config.constants.UserCacheKeys;
import dev.flyfish.framework.utils.Assert; import dev.flyfish.framework.utils.Assert;
import dev.flyfish.framework.utils.ReactiveRedisOperations; import dev.flyfish.framework.utils.ReactiveRedisOperations;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.springframework.security.authentication.*; import org.springframework.security.authentication.*;
@ -23,7 +24,6 @@ import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import jakarta.annotation.Resource;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;

View File

@ -1,28 +1,15 @@
package dev.flyfish.framework.user.service; package dev.flyfish.framework.user.service;
import dev.flyfish.framework.user.domain.AdminUserDetails;
import dev.flyfish.framework.domain.base.IUser; import dev.flyfish.framework.domain.base.IUser;
import dev.flyfish.framework.domain.po.Department;
import dev.flyfish.framework.domain.po.Role;
import dev.flyfish.framework.enums.UserType;
import dev.flyfish.framework.utils.CopyUtils;
import dev.flyfish.framework.utils.DepartUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.*; /**
import java.util.stream.Collectors; * 用户详情转换器
import java.util.stream.Stream; *
* @author wangyu
@Service */
@RequiredArgsConstructor public interface UserDetailsConverter {
public class UserDetailsConverter {
private final DepartmentService departmentService;
/** /**
* 将用户映射为用户详情查询所有权限code * 将用户映射为用户详情查询所有权限code
@ -30,72 +17,5 @@ public class UserDetailsConverter {
* @param user 用户信息 * @param user 用户信息
* @return 结果 * @return 结果
*/ */
public Mono<UserDetails> mapToUserDetails(IUser user) { Mono<UserDetails> mapToUserDetails(IUser user);
AdminUserDetails userDetail = new AdminUserDetails();
CopyUtils.copyProps(user, userDetail);
userDetail.setAuthorityCodes(Collections.singleton(Department.PUBLIC));
if (user.getType() == UserType.SUPER_ADMIN) {
userDetail.setAuthorityCodes(null);
} else if (CollectionUtils.isNotEmpty(user.getDepartments())) {
return judgeDeparts(user, userDetail);
}
return Mono.just(userDetail);
}
/**
* 判断部门包含关系默认看不到本部门的数据却可以看到子部门的
*
* @param user 用户信息
* @param userDetails 用户详情
*/
private Mono<UserDetails> judgeDeparts(IUser user, AdminUserDetails userDetails) {
// 整合数据权限
Set<Role.Authority> authorities = user.getRoles().stream()
.flatMap(role -> null == role.getAuthorities() ? Stream.empty() : role.getAuthorities().stream())
.collect(Collectors.toSet());
// 默认使用用户当前选择的权限进行查询
String authority = user.getAuthority();
// 根据权限组装查询条件
Set<String> departs = StringUtils.isNotBlank(authority) ? Collections.singleton(authority) :
DepartUtils.mergeDeparts(user.getDepartments());
// 查询所有子部门id
return departmentService.getSubCodes(departs)
.map(codes -> {
// 取出权限便于判定
boolean admin = authorities.contains(Role.Authority.ADMIN);
boolean view = authorities.contains(Role.Authority.VIEW);
boolean viewChildren = authorities.contains(Role.Authority.VIEW_CHILDREN);
// 全部
Set<String> all = new HashSet<>();
// 拥有查看本部门的权限
if (admin || view) {
all.addAll(departs);
}
// 拥有查看所有下级的权限
if (admin || viewChildren) {
all.addAll(codes);
}
// 最终设置权限
if (CollectionUtils.isNotEmpty(all)) {
userDetails.setAuthorityCodes(union(all, userDetails.getAuthorityCodes()));
}
userDetails.setVisibleDeparts(union(codes, departs));
return userDetails;
});
}
/**
* 链接两个set
*
* @param a 第一个
* @param b 第二个
* @return 结果
*/
private Set<String> union(Set<String> a, Set<String> b) {
Set<String> result = new HashSet<>();
result.addAll(a);
result.addAll(b);
return result;
}
} }