⚡ 优化权限逻辑.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.orion.visor.framework.common.meta;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.orion.lang.id.UUIds;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
/**
|
||||
* traceId 持有者
|
||||
@@ -23,16 +25,74 @@ public class TraceIdHolder {
|
||||
*/
|
||||
private static final ThreadLocal<String> HOLDER = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获取 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String get() {
|
||||
return HOLDER.get();
|
||||
}
|
||||
|
||||
public static void set(String traceId) {
|
||||
HOLDER.set(traceId);
|
||||
/**
|
||||
* 设置 traceId
|
||||
*/
|
||||
public static void set() {
|
||||
set(createTraceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 traceId
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void set(String traceId) {
|
||||
// 设置应用上下文
|
||||
HOLDER.set(traceId);
|
||||
// 设置日志上下文
|
||||
setMdc(traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 traceId
|
||||
*/
|
||||
public static void remove() {
|
||||
// 移除应用上下文
|
||||
HOLDER.remove();
|
||||
// 移除日志上下文
|
||||
removeMdc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从应用上下文 设置到日志上下文
|
||||
*/
|
||||
public static void setMdc() {
|
||||
setMdc(HOLDER.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置到日志上下文
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void setMdc(String traceId) {
|
||||
MDC.put(TRACE_ID_MDC, traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除日志上下文
|
||||
*/
|
||||
public static void removeMdc() {
|
||||
MDC.remove(TRACE_ID_MDC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String createTraceId() {
|
||||
return UUIds.random32();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,13 @@ public class SecurityUtils {
|
||||
return loginUser != null ? loginUser.getTimestamp() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空用户上下文
|
||||
*/
|
||||
public static void clearAuthentication() {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前用户
|
||||
*
|
||||
@@ -107,7 +114,9 @@ public class SecurityUtils {
|
||||
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
|
||||
// 创建 authentication
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
if (request != null) {
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
}
|
||||
// 设置上下文
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.orion.visor.framework.web.core.filter;
|
||||
|
||||
import com.orion.lang.id.UUIds;
|
||||
import com.orion.visor.framework.common.meta.TraceIdHolder;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
@@ -23,21 +21,17 @@ public class TraceIdFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
try {
|
||||
// 获 traceId
|
||||
String traceId = UUIds.random32();
|
||||
// 设置应用上下文
|
||||
// 获取 traceId
|
||||
String traceId = TraceIdHolder.createTraceId();
|
||||
// 设置 traceId 上下文
|
||||
TraceIdHolder.set(traceId);
|
||||
// 设置日志上下文
|
||||
MDC.put(TraceIdHolder.TRACE_ID_MDC, traceId);
|
||||
// 设置响应头
|
||||
response.setHeader(TraceIdHolder.TRACE_ID_HEADER, traceId);
|
||||
// 执行请求
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
// 清理应用上下文
|
||||
// 清空 traceId 上下文
|
||||
TraceIdHolder.remove();
|
||||
// 清理日志上下文
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,8 @@ app:
|
||||
allow-refresh: true
|
||||
# 凭证续签最大次数
|
||||
max-refresh-count: 3
|
||||
# 登录失败发送站内信阈值
|
||||
login-failed-send-threshold: 3
|
||||
# 登录失败锁定次数
|
||||
login-failed-lock-count: 5
|
||||
# 登录失败锁定时间 (分)
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.orion.lang.utils.Strings;
|
||||
import com.orion.net.host.SessionHolder;
|
||||
import com.orion.net.host.SessionLogger;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.visor.framework.common.constant.AppConst;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.utils.CryptoUtils;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
@@ -43,6 +44,8 @@ public class SessionStores {
|
||||
SessionHolder sessionHolder = SessionHolder.create();
|
||||
sessionHolder.setLogger(SessionLogger.INFO);
|
||||
SessionStore session = createSessionStore(conn, sessionHolder);
|
||||
// 设置版本
|
||||
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
|
||||
// 连接
|
||||
session.connect();
|
||||
log.info("SessionStores-open-success hostId: {}, address: {}, username: {}", hostId, address, username);
|
||||
|
||||
@@ -65,13 +65,15 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
||||
// 移除会话连接信息
|
||||
channel.getAttributes().remove(sessionId);
|
||||
Exception ex = null;
|
||||
ITerminalSession session = null;
|
||||
try {
|
||||
// 连接主机
|
||||
ITerminalSession session = this.connect(sessionId, connect, channel, payload);
|
||||
session = this.connect(sessionId, connect, channel, payload);
|
||||
// 添加会话到 manager
|
||||
hostTerminalManager.addSession(session);
|
||||
} catch (Exception e) {
|
||||
ex = e;
|
||||
Streams.close(session);
|
||||
// 修改连接状态为失败
|
||||
Map<String, Object> extra = Maps.newMap(4);
|
||||
extra.put(ExtraFieldConst.ERROR_MESSAGE, this.getConnectErrorMessage(e));
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserAuthDTO;
|
||||
|
||||
/**
|
||||
* 认证服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:37
|
||||
*/
|
||||
public interface AuthenticationApi {
|
||||
|
||||
/**
|
||||
* 通过密码认证
|
||||
*
|
||||
* @param username username
|
||||
* @param password password
|
||||
* @param addFailedCount addFailedCount
|
||||
* @return result
|
||||
*/
|
||||
SystemUserAuthDTO authByPassword(String username, String password, boolean addFailedCount);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限 对外服务类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/19 15:22
|
||||
*/
|
||||
public interface PermissionApi {
|
||||
|
||||
/**
|
||||
* 用户是否为管理员用户
|
||||
*
|
||||
* @param id id
|
||||
* @return isAdmin
|
||||
*/
|
||||
boolean isAdminUser(Long id);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色
|
||||
*
|
||||
* @param userId userId
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(Long userId, String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色
|
||||
*
|
||||
* @param userId userId
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(Long userId, List<String> roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(Long userId, String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(Long userId, List<String> permissions);
|
||||
|
||||
}
|
||||
@@ -11,6 +11,22 @@ import com.orion.visor.module.infra.entity.dto.user.SystemUserDTO;
|
||||
*/
|
||||
public interface SystemUserApi {
|
||||
|
||||
/**
|
||||
* 通过 id 查询用户名
|
||||
*
|
||||
* @param id id
|
||||
* @return username
|
||||
*/
|
||||
String getUsernameById(Long id);
|
||||
|
||||
/**
|
||||
* 通过 id 查询花名
|
||||
*
|
||||
* @param id id
|
||||
* @return nickname
|
||||
*/
|
||||
String getNicknameById(Long id);
|
||||
|
||||
/**
|
||||
* 通过 id 查询用户
|
||||
*
|
||||
@@ -19,12 +35,4 @@ public interface SystemUserApi {
|
||||
*/
|
||||
SystemUserDTO getUserById(Long id);
|
||||
|
||||
/**
|
||||
* 用户是否为管理员用户
|
||||
*
|
||||
* @param id id
|
||||
* @return isAdmin
|
||||
*/
|
||||
boolean isAdminUser(Long id);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.orion.visor.module.infra.entity.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户状态检查 业务对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:52
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "SystemUserAuthDTO", description = "用户认证 业务对象")
|
||||
public class SystemUserAuthDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "花名")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "密码是否正确")
|
||||
private Boolean passRight;
|
||||
|
||||
@Schema(description = "认证是否通过")
|
||||
private Boolean authed;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.orion.visor.module.infra.api.impl;
|
||||
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.module.infra.api.AuthenticationApi;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserAuthDTO;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 认证服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AuthenticationApiImpl implements AuthenticationApi {
|
||||
|
||||
@Resource
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Override
|
||||
public SystemUserAuthDTO authByPassword(String username, String password, boolean addFailedCount) {
|
||||
SystemUserAuthDTO result = new SystemUserAuthDTO();
|
||||
try {
|
||||
// 登录预检
|
||||
SystemUserDO user = authenticationService.preCheckLogin(username, password);
|
||||
result.setId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setNickname(user.getNickname());
|
||||
// 检查用户密码
|
||||
boolean passRight = authenticationService.checkUserPassword(user, password, addFailedCount);
|
||||
result.setPassRight(passRight);
|
||||
Valid.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 检查用户状态
|
||||
authenticationService.checkUserStatus(user);
|
||||
result.setAuthed(true);
|
||||
} catch (Exception e) {
|
||||
result.setAuthed(false);
|
||||
result.setErrorMessage(e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.orion.visor.module.infra.api.impl;
|
||||
|
||||
import com.orion.visor.module.infra.api.PermissionApi;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限 对外服务类实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/19 15:25
|
||||
*/
|
||||
@Service
|
||||
public class PermissionApiImpl implements PermissionApi {
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long id) {
|
||||
return permissionService.isAdminUser(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(Long userId, String role) {
|
||||
return permissionService.hasRole(userId, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(Long userId, List<String> roles) {
|
||||
return permissionService.hasAnyRole(userId, roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, String permission) {
|
||||
return permissionService.hasPermission(userId, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, List<String> permissions) {
|
||||
return permissionService.hasAnyPermission(userId, permissions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import com.orion.visor.module.infra.convert.SystemUserProviderConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserDTO;
|
||||
import com.orion.visor.module.infra.service.SystemUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -23,8 +22,25 @@ public class SystemUserApiImpl implements SystemUserApi {
|
||||
@Resource
|
||||
private SystemUserDAO systemUserDAO;
|
||||
|
||||
@Resource
|
||||
private SystemUserService systemUserService;
|
||||
@Override
|
||||
public String getUsernameById(Long id) {
|
||||
return systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.select(SystemUserDO::getUsername)
|
||||
.eq(SystemUserDO::getId, id)
|
||||
.then()
|
||||
.getOne(SystemUserDO::getUsername);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNicknameById(Long id) {
|
||||
return systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.select(SystemUserDO::getNickname)
|
||||
.eq(SystemUserDO::getId, id)
|
||||
.then()
|
||||
.getOne(SystemUserDO::getNickname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUserDTO getUserById(Long id) {
|
||||
@@ -35,9 +51,4 @@ public class SystemUserApiImpl implements SystemUserApi {
|
||||
return SystemUserProviderConvert.MAPPER.to(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long id) {
|
||||
return systemUserService.isAdminUser(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.visor.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -26,22 +26,23 @@ import java.util.List;
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 11:20
|
||||
*/
|
||||
@Tag(name = "infra - 权限服务")
|
||||
@Tag(name = "infra - 用户权限服务")
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestWrapper
|
||||
@RestController
|
||||
@RequestMapping("/infra/permission")
|
||||
public class PermissionController {
|
||||
@RequestMapping("/infra/user-permission")
|
||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||
public class UserPermissionController {
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@PutMapping("/refresh-cache")
|
||||
@Operation(summary = "刷新角色权限缓存")
|
||||
@PreAuthorize("@ss.hasPermission('infra:system-menu:management:refresh-cache')")
|
||||
public Boolean refreshCache() {
|
||||
permissionService.initPermissionCache();
|
||||
userPermissionService.initPermissionCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,14 +50,14 @@ public class PermissionController {
|
||||
@GetMapping("/menu")
|
||||
@Operation(summary = "获取用户菜单")
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
return permissionService.getUserMenuList();
|
||||
return userPermissionService.getUserMenuList();
|
||||
}
|
||||
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@GetMapping("/user")
|
||||
@Operation(summary = "获取用户权限聚合信息")
|
||||
public UserPermissionVO getUserPermission() {
|
||||
return permissionService.getUserPermission();
|
||||
return userPermissionService.getUserPermission();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,11 +34,22 @@ public interface SystemRoleDAO extends IMapper<SystemRoleDO> {
|
||||
/**
|
||||
* 通过 userId 和 roleCode 查询 roleId (检查用户是否包含某个角色)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param code code
|
||||
* @param userId userId
|
||||
* @param codeList codeList
|
||||
* @return roleId
|
||||
*/
|
||||
Long getRoleIdByUserIdAndRoleCode(@Param("userId") Long userId, @Param("code") String code);
|
||||
List<Long> getRoleIdByUserIdAndRoleCode(@Param("userId") Long userId,
|
||||
@Param("codeList") List<String> codeList);
|
||||
|
||||
/**
|
||||
* 通过 roleId 和 permission 查询 permission (检查角色是否包含某个权限)
|
||||
*
|
||||
* @param roleIdList roleIdList
|
||||
* @param permissionList permissionList
|
||||
* @return permission
|
||||
*/
|
||||
List<String> getPermissionByRoleIdAndPermission(@Param("roleIdList") List<Long> roleIdList,
|
||||
@Param("permissionList") List<String> permissionList);
|
||||
|
||||
/**
|
||||
* 查询用户角色
|
||||
|
||||
@@ -31,6 +31,11 @@ public class AppAuthenticationConfig {
|
||||
*/
|
||||
private Integer maxRefreshCount;
|
||||
|
||||
/**
|
||||
* 登录失败发送站内信阈值
|
||||
*/
|
||||
private Integer loginFailedSendThreshold;
|
||||
|
||||
/**
|
||||
* 登录失败锁定次数
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.orion.visor.module.infra.define.message;
|
||||
|
||||
import com.orion.visor.module.infra.define.SystemMessageDefine;
|
||||
import com.orion.visor.module.infra.enums.MessageClassifyEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户 系统消息定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/5/14 17:23
|
||||
*/
|
||||
@Getter
|
||||
public enum SystemUserMessageDefine implements SystemMessageDefine {
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
LOGIN_FAILED(MessageClassifyEnum.NOTICE,
|
||||
"登录失败",
|
||||
"您的账号在 <sb>${time}</sb> 登录系统时身份认证失败, 您的密码可能已经泄漏。如非本人操作请尽快修改密码。(<sb>${address} - ${location}</sb>)"),
|
||||
|
||||
;
|
||||
|
||||
SystemUserMessageDefine(MessageClassifyEnum classify, String title, String content) {
|
||||
this.classify = classify;
|
||||
this.type = this.name();
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息分类
|
||||
*/
|
||||
private final MessageClassifyEnum classify;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private final String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private final String content;
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -27,30 +27,30 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 检查是否有权限
|
||||
return permissionService.hasPermission(permission);
|
||||
return userPermissionService.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
// 检查是否有权限
|
||||
return permissionService.hasAnyPermission(permissions);
|
||||
return userPermissionService.hasAnyPermission(permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 检查是否有角色
|
||||
return permissionService.hasRole(role);
|
||||
return userPermissionService.hasRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
// 检查是否有角色
|
||||
return permissionService.hasAnyRole(roles);
|
||||
return userPermissionService.hasAnyRole(roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.entity.request.user.UserLoginRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.UserLoginVO;
|
||||
@@ -48,4 +49,30 @@ public interface AuthenticationService {
|
||||
*/
|
||||
LoginTokenDTO getLoginTokenInfo(String loginToken);
|
||||
|
||||
/**
|
||||
* 登录预检查
|
||||
*
|
||||
* @param username username
|
||||
* @param password password
|
||||
* @return user
|
||||
*/
|
||||
SystemUserDO preCheckLogin(String username, String password);
|
||||
|
||||
/**
|
||||
* 检查用户密码
|
||||
*
|
||||
* @param user user
|
||||
* @param password password
|
||||
* @param addFailedCount addFailedCount
|
||||
* @return passRight
|
||||
*/
|
||||
boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount);
|
||||
|
||||
/**
|
||||
* 检查用户状态
|
||||
*
|
||||
* @param user user
|
||||
*/
|
||||
void checkUserStatus(SystemUserDO user);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,92 +1,58 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:03
|
||||
* @since 2024/8/19 15:29
|
||||
*/
|
||||
public interface PermissionService {
|
||||
|
||||
/**
|
||||
* 获取 角色缓存
|
||||
* 检测用户是否是为管理员
|
||||
*
|
||||
* @return cache
|
||||
* @param userId userId
|
||||
* @return 是否为管理员
|
||||
*/
|
||||
Map<Long, SystemRoleDO> getRoleCache();
|
||||
boolean isAdminUser(Long userId);
|
||||
|
||||
/**
|
||||
* 获取 菜单缓存 以作角色权限直接引用
|
||||
* 检查当前用户是否含有此角色
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
List<SystemMenuCacheDTO> getMenuCache();
|
||||
|
||||
/**
|
||||
* 获取 角色菜单关联
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, List<SystemMenuCacheDTO>> getRoleMenuCache();
|
||||
|
||||
/**
|
||||
* 初始化权限缓存
|
||||
*/
|
||||
void initPermissionCache();
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色 (有效性判断)
|
||||
*
|
||||
* @param role role
|
||||
* @param userId userId
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(String role);
|
||||
boolean hasRole(Long userId, String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色 (有效性判断)
|
||||
* 检查当前用户是否含有任意角色
|
||||
*
|
||||
* @param roles roles
|
||||
* @param userId userId
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(String... roles);
|
||||
boolean hasAnyRole(Long userId, List<String> roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限 (有效性判断)
|
||||
* 检查当前用户是否含有此权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
boolean hasPermission(Long userId, String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限 (有效性判断)
|
||||
* 检查当前用户是否含任意权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(String... permissions);
|
||||
|
||||
/**
|
||||
* 获取用户菜单
|
||||
*
|
||||
* @return 菜单
|
||||
*/
|
||||
List<SystemMenuVO> getUserMenuList();
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
*
|
||||
* @return 权限信息
|
||||
*/
|
||||
UserPermissionVO getUserPermission();
|
||||
boolean hasAnyPermission(Long userId, List<String> permissions);
|
||||
|
||||
}
|
||||
|
||||
@@ -92,12 +92,4 @@ public interface SystemUserService {
|
||||
*/
|
||||
void resetPassword(UserResetPasswordRequest request);
|
||||
|
||||
/**
|
||||
* 检测用户是否是为管理员
|
||||
*
|
||||
* @param userId userId
|
||||
* @return 是否为管理员
|
||||
*/
|
||||
boolean isAdminUser(Long userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:03
|
||||
*/
|
||||
public interface UserPermissionService {
|
||||
|
||||
/**
|
||||
* 获取 角色缓存
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, SystemRoleDO> getRoleCache();
|
||||
|
||||
/**
|
||||
* 获取 菜单缓存 以作角色权限直接引用
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
List<SystemMenuCacheDTO> getMenuCache();
|
||||
|
||||
/**
|
||||
* 获取 角色菜单关联
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, List<SystemMenuCacheDTO>> getRoleMenuCache();
|
||||
|
||||
/**
|
||||
* 初始化权限缓存
|
||||
*/
|
||||
void initPermissionCache();
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色 (有效性判断)
|
||||
*
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色 (有效性判断)
|
||||
*
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(String... roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限 (有效性判断)
|
||||
*
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限 (有效性判断)
|
||||
*
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(String... permissions);
|
||||
|
||||
/**
|
||||
* 获取用户菜单
|
||||
*
|
||||
* @return 菜单
|
||||
*/
|
||||
List<SystemMenuVO> getUserMenuList();
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
*
|
||||
* @return 权限信息
|
||||
*/
|
||||
UserPermissionVO getUserPermission();
|
||||
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.annotation.Keep;
|
||||
import com.orion.lang.define.wrapper.Pair;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.crypto.Signatures;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.annotation.Keep;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.common.utils.CryptoUtils;
|
||||
@@ -17,30 +20,30 @@ import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.framework.redis.core.utils.RedisStrings;
|
||||
import com.orion.visor.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.api.SystemMessageApi;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserRoleDAO;
|
||||
import com.orion.visor.module.infra.define.cache.UserCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.config.AppAuthenticationConfig;
|
||||
import com.orion.visor.module.infra.define.message.SystemUserMessageDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenIdentityDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.message.SystemMessageDTO;
|
||||
import com.orion.visor.module.infra.entity.request.user.UserLoginRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.UserLoginVO;
|
||||
import com.orion.visor.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -64,7 +67,10 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
private SystemUserRoleDAO systemUserRoleDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Resource
|
||||
private SystemMessageApi systemMessageApi;
|
||||
|
||||
@Keep
|
||||
@Resource
|
||||
@@ -72,39 +78,35 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
|
||||
@Override
|
||||
public UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest) {
|
||||
// 设置日志上下文的用户 否则登录失败不会记录日志
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(request));
|
||||
// 登录前检查
|
||||
this.preCheckLogin(request);
|
||||
// 获取登录用户
|
||||
SystemUserDO user = systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.eq(SystemUserDO::getUsername, request.getUsername())
|
||||
.then()
|
||||
.getOne();
|
||||
Valid.notNull(user, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 重新设置日志上下文
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(user));
|
||||
// 检查密码
|
||||
boolean passwordCorrect = this.checkPassword(request, user);
|
||||
Valid.isTrue(passwordCorrect, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 检查用户状态
|
||||
UserStatusEnum.checkUserStatus(user.getStatus());
|
||||
// 设置上次登录时间
|
||||
this.setLastLoginTime(user.getId());
|
||||
// 删除用户缓存
|
||||
this.deleteUserCache(user);
|
||||
// 重设用户缓存
|
||||
this.setUserCache(user);
|
||||
// 获取登录信息
|
||||
String remoteAddr = IpUtils.getRemoteAddr(servletRequest);
|
||||
String location = IpUtils.getLocation(remoteAddr);
|
||||
String userAgent = Servlets.getUserAgent(servletRequest);
|
||||
// 设置日志上下文的用户 否则登录失败不会记录日志
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(request));
|
||||
// 登录前检查
|
||||
SystemUserDO user = this.preCheckLogin(request.getUsername(), request.getPassword());
|
||||
// 重新设置日志上下文
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(user));
|
||||
// 用户密码校验
|
||||
boolean passRight = this.checkUserPassword(user, request.getPassword(), true);
|
||||
// 发送站内信
|
||||
this.sendLoginFailedErrorMessage(passRight, user, remoteAddr, location);
|
||||
Valid.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 用户状态校验
|
||||
this.checkUserStatus(user);
|
||||
Long id = user.getId();
|
||||
// 设置上次登录时间
|
||||
this.setLastLoginTime(id);
|
||||
// 删除用户缓存
|
||||
this.deleteUserCache(user);
|
||||
// 重设用户缓存
|
||||
this.setUserCache(user);
|
||||
long current = System.currentTimeMillis();
|
||||
// 不允许多端登录
|
||||
if (!appAuthenticationConfig.getAllowMultiDevice()) {
|
||||
// 无效化其他缓存
|
||||
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location, userAgent);
|
||||
this.invalidOtherDeviceToken(id, current, remoteAddr, location, userAgent);
|
||||
}
|
||||
// 生成 loginToken
|
||||
String token = this.generatorLoginToken(user, current, remoteAddr, location, userAgent);
|
||||
@@ -189,62 +191,83 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 token pair
|
||||
*
|
||||
* @param loginToken loginToken
|
||||
* @return pair
|
||||
*/
|
||||
private Pair<Long, Long> getLoginTokenPair(String loginToken) {
|
||||
if (loginToken == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String value = CryptoUtils.decryptBase62(loginToken);
|
||||
String[] pair = value.split(":");
|
||||
return Pair.of(Long.valueOf(pair[0]), Long.valueOf(pair[1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录预检查
|
||||
*
|
||||
* @param request request
|
||||
*/
|
||||
private void preCheckLogin(UserLoginRequest request) {
|
||||
@Override
|
||||
public SystemUserDO preCheckLogin(String username, String password) {
|
||||
// 检查密码长度是否正确 MD5 长度为 32
|
||||
if (request.getPassword().length() != Const.MD5_LEN) {
|
||||
if (password.length() != Const.MD5_LEN) {
|
||||
throw Exceptions.argument(ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
}
|
||||
// 检查登录失败次数
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(username);
|
||||
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCount != null
|
||||
&& Integer.parseInt(failedCount) >= appAuthenticationConfig.getLoginFailedLockCount()) {
|
||||
throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED);
|
||||
}
|
||||
// 获取登录用户
|
||||
SystemUserDO user = systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.eq(SystemUserDO::getUsername, username)
|
||||
.then()
|
||||
.getOne();
|
||||
Valid.notNull(user, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount) {
|
||||
// 检查密码
|
||||
boolean passRight = user.getPassword().equals(Signatures.md5(password));
|
||||
if (!passRight && addFailedCount) {
|
||||
// 刷新登录失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||
redisTemplate.opsForValue().increment(failedCountKey);
|
||||
RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
|
||||
}
|
||||
return passRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUserStatus(SystemUserDO user) {
|
||||
// 检查用户状态
|
||||
UserStatusEnum.checkUserStatus(user.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码
|
||||
* 发送登录失败错误消息
|
||||
*
|
||||
* @param request request
|
||||
* @param user user
|
||||
* @return 是否正确
|
||||
* @param passRight passRight
|
||||
* @param user user
|
||||
* @param remoteAddr remoteAddr
|
||||
* @param location location
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
private boolean checkPassword(UserLoginRequest request, SystemUserDO user) {
|
||||
// 密码正确
|
||||
if (user.getPassword().equals(Signatures.md5(request.getPassword()))) {
|
||||
return true;
|
||||
private void sendLoginFailedErrorMessage(boolean passRight, SystemUserDO user,
|
||||
String remoteAddr, String location) {
|
||||
if (passRight) {
|
||||
return;
|
||||
}
|
||||
// 刷新登录失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
redisTemplate.opsForValue().increment(failedCountKey);
|
||||
RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
|
||||
return false;
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||
String failedCountStr = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCountStr == null || !Strings.isInteger(failedCountStr)) {
|
||||
return;
|
||||
}
|
||||
// 直接用相等 因为只触发一次
|
||||
if (!appAuthenticationConfig.getLoginFailedSendThreshold().equals(Integer.valueOf(failedCountStr))) {
|
||||
return;
|
||||
}
|
||||
// 发送站内信
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(ExtraFieldConst.ADDRESS, remoteAddr);
|
||||
params.put(ExtraFieldConst.LOCATION, location);
|
||||
params.put(ExtraFieldConst.TIME, Dates.current());
|
||||
SystemMessageDTO message = SystemMessageDTO.builder()
|
||||
.receiverId(user.getId())
|
||||
.receiverUsername(user.getUsername())
|
||||
.relKey(user.getUsername())
|
||||
.params(params)
|
||||
.build();
|
||||
// 发送
|
||||
systemMessageApi.create(SystemUserMessageDefine.LOGIN_FAILED, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +296,25 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
redisTemplate.delete(Lists.of(userInfoKey, loginFailedCountKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 token pair
|
||||
*
|
||||
* @param loginToken loginToken
|
||||
* @return pair
|
||||
*/
|
||||
private Pair<Long, Long> getLoginTokenPair(String loginToken) {
|
||||
if (loginToken == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String value = CryptoUtils.decryptBase62(loginToken);
|
||||
String[] pair = value.split(":");
|
||||
return Pair.of(Long.valueOf(pair[0]), Long.valueOf(pair[1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户缓存
|
||||
*
|
||||
@@ -283,7 +325,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
Long id = user.getId();
|
||||
// 查询用户角色
|
||||
List<Long> roleIds = systemUserRoleDAO.selectRoleIdByUserId(id);
|
||||
List<UserRole> roleList = permissionService.getRoleCache()
|
||||
List<UserRole> roleList = userPermissionService.getRoleCache()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(s -> roleIds.contains(s.getId()))
|
||||
|
||||
@@ -1,305 +1,67 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.convert.SystemMenuConvert;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemMenuDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleMenuDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemMenuDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserCollectInfoVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.PreferenceTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.PreferenceService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import com.orion.visor.module.infra.service.TipsService;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 权限服务
|
||||
* 权限 服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:05
|
||||
* @since 2024/8/19 15:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
@Getter
|
||||
private final Map<Long, SystemRoleDO> roleCache = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private final List<SystemMenuCacheDTO> menuCache = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final Map<Long, List<SystemMenuCacheDTO>> roleMenuCache = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private SystemRoleDAO systemRoleDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuDAO systemMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuService systemMenuService;
|
||||
|
||||
@Resource
|
||||
private PreferenceService preferenceService;
|
||||
|
||||
@Resource
|
||||
private TipsService tipsService;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void initPermissionCache() {
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("initPermissionCache-start");
|
||||
roleCache.clear();
|
||||
menuCache.clear();
|
||||
roleMenuCache.clear();
|
||||
// 加载所有角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectList(null);
|
||||
for (SystemRoleDO role : roles) {
|
||||
roleCache.put(role.getId(), role);
|
||||
}
|
||||
// 加载所有菜单信息
|
||||
List<SystemMenuDO> menuList = systemMenuDAO.selectList(null);
|
||||
List<SystemMenuCacheDTO> menus = SystemMenuConvert.MAPPER.toCache(menuList);
|
||||
Map<Long, SystemMenuCacheDTO> menuMapping = menus.stream()
|
||||
.collect(Collectors.toMap(SystemMenuCacheDTO::getId, Function.identity()));
|
||||
menuCache.addAll(menus);
|
||||
// 查询所有角色菜单
|
||||
systemRoleMenuDAO.selectList(null)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SystemRoleMenuDO::getRoleId,
|
||||
Collectors.mapping(SystemRoleMenuDO::getMenuId, Collectors.toList())))
|
||||
.forEach((roleId, menuIdList) -> {
|
||||
// 获取菜单引用
|
||||
List<SystemMenuCacheDTO> roleMenus = menuIdList.stream()
|
||||
.map(menuMapping::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// 获取角色引用
|
||||
roleMenuCache.put(roleId, roleMenus);
|
||||
});
|
||||
log.info("initPermissionCache-end used: {}ms", System.currentTimeMillis() - start);
|
||||
public boolean isAdminUser(Long userId) {
|
||||
return this.hasAnyRole(userId, Lists.of(RoleDefine.ADMIN_CODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
public boolean hasRole(Long userId, String role) {
|
||||
return this.hasAnyRole(userId, Lists.of(role));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(Long userId, List<String> roles) {
|
||||
return !systemRoleDAO.getRoleIdByUserIdAndRoleCode(userId, roles).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, String permission) {
|
||||
return this.hasAnyPermission(userId, Lists.singleton(permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, List<String> permissions) {
|
||||
// 查询用户角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectRoleByUserId(userId);
|
||||
roles.removeIf(s -> !RoleStatusEnum.ENABLED.getStatus().equals(s.getStatus()));
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员或包含此角色
|
||||
return RoleDefine.containsAdmin(roles.values()) || roles.containsValue(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
if (Arrays1.isEmpty(roles)) {
|
||||
// 判断是否为 admin
|
||||
boolean isAdmin = roles.stream().anyMatch(s -> s.getCode().equals(RoleDefine.ADMIN_CODE));
|
||||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> enableRoles = this.getUserEnabledRoles();
|
||||
if (enableRoles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员 || 有此角色
|
||||
return RoleDefine.containsAdmin(enableRoles.values())
|
||||
|| Arrays.stream(roles).anyMatch(enableRoles::containsValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查普通角色是否有此权限
|
||||
return roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
if (Arrays1.isEmpty(permissions)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查用户角色是否包含权限
|
||||
return Arrays.stream(permissions)
|
||||
.anyMatch(perm -> roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, perm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return Lists.empty();
|
||||
}
|
||||
// 查询角色菜单
|
||||
Stream<SystemMenuCacheDTO> mergeStream;
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部菜单
|
||||
mergeStream = menuCache.stream();
|
||||
} else {
|
||||
// 当前用户所适配的角色菜单
|
||||
mergeStream = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct();
|
||||
}
|
||||
// 状态过滤
|
||||
List<SystemMenuVO> menus = mergeStream
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.filter(s -> !MenuTypeEnum.FUNCTION.getType().equals(s.getType()))
|
||||
.map(SystemMenuConvert.MAPPER::to)
|
||||
List<Long> roleIdList = roles.stream()
|
||||
.map(SystemRoleDO::getId)
|
||||
.collect(Collectors.toList());
|
||||
// 构建菜单树
|
||||
return systemMenuService.buildSystemMenuTree(menus);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public UserPermissionVO getUserPermission() {
|
||||
// 获取用户信息
|
||||
UserCollectInfoVO user = SystemUserConvert.MAPPER.toCollectInfo(SecurityUtils.getLoginUser());
|
||||
Long id = user.getId();
|
||||
// 获取用户系统偏好
|
||||
Future<Map<String, Object>> systemPreference = preferenceService.getPreferenceAsync(id, PreferenceTypeEnum.SYSTEM);
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
// 获取用户权限
|
||||
List<String> permissions;
|
||||
if (roles.isEmpty()) {
|
||||
permissions = Lists.empty();
|
||||
} else {
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部权限
|
||||
permissions = Lists.of(Const.ASTERISK);
|
||||
} else {
|
||||
// 当前用户所适配的角色的权限
|
||||
permissions = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
// 设置已提示的 key
|
||||
user.setTippedKeys(tipsService.getTippedKeys());
|
||||
// 获取异步结果
|
||||
user.setSystemPreference(systemPreference.get());
|
||||
// 组装数据
|
||||
return UserPermissionVO.builder()
|
||||
.user(user)
|
||||
.roles(roles.values())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查角色是否有权限
|
||||
*
|
||||
* @param roleId roleId
|
||||
* @param permission permission
|
||||
* @return 是否有权限
|
||||
*/
|
||||
private boolean checkRoleHasPermission(Long roleId, String permission) {
|
||||
// 获取角色权限列表
|
||||
List<SystemMenuCacheDTO> menus = roleMenuCache.get(roleId);
|
||||
if (Lists.isEmpty(menus)) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否有此权限
|
||||
return menus.stream()
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(permission::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户启用的角色
|
||||
*
|
||||
* @return roles
|
||||
*/
|
||||
private Map<Long, String> getUserEnabledRoles() {
|
||||
// 获取当前用户角色
|
||||
List<UserRole> userRoles = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getRoles)
|
||||
.orElse(Lists.empty());
|
||||
if (Lists.isEmpty(userRoles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
// 获取角色编码
|
||||
Map<Long, String> roles = userRoles.stream()
|
||||
.map(UserRole::getId)
|
||||
.map(roleCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
// 过滤未启用的角色
|
||||
.filter(r -> RoleStatusEnum.ENABLED.getStatus().equals(r.getStatus()))
|
||||
.collect(Collectors.toMap(SystemRoleDO::getId, SystemRoleDO::getCode));
|
||||
if (Maps.isEmpty(roles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
return roles;
|
||||
return !systemRoleDAO.getPermissionByRoleIdAndPermission(roleIdList, permissions).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuVisibleEnum;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -51,7 +51,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
public Long createSystemMenu(SystemMenuCreateRequest request) {
|
||||
@@ -68,7 +68,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
int effect = systemMenuDAO.insert(record);
|
||||
log.info("SystemMenuService-createSystemMenu effect: {}, record: {}", effect, JSON.toJSONString(record));
|
||||
// 保存至缓存
|
||||
List<SystemMenuCacheDTO> menuCache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> menuCache = userPermissionService.getMenuCache();
|
||||
menuCache.add(SystemMenuConvert.MAPPER.toCache(record));
|
||||
return record.getId();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 重新查询转换为缓存
|
||||
SystemMenuCacheDTO cache = SystemMenuConvert.MAPPER.toCache(systemMenuDAO.selectById(id));
|
||||
// 获取原始缓存
|
||||
permissionService.getMenuCache()
|
||||
userPermissionService.getMenuCache()
|
||||
.stream()
|
||||
.filter(s -> s.getId().equals(id))
|
||||
.findFirst()
|
||||
@@ -115,7 +115,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
Integer type = request.getType();
|
||||
Integer status = request.getStatus();
|
||||
// 从缓存中查询
|
||||
List<SystemMenuVO> menus = permissionService.getMenuCache()
|
||||
List<SystemMenuVO> menus = userPermissionService.getMenuCache()
|
||||
.stream()
|
||||
.filter(s -> Strings.isBlank(name) || s.getName().contains(name))
|
||||
.filter(s -> type == null || s.getType().equals(type))
|
||||
@@ -197,7 +197,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 添加日志参数
|
||||
OperatorLogs.add(OperatorLogs.NAME, record.getName());
|
||||
// 从缓存中查询
|
||||
List<SystemMenuCacheDTO> cache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> cache = userPermissionService.getMenuCache();
|
||||
// 获取要更新的id
|
||||
List<Long> updateIdList = this.getChildrenIdList(id, cache, record.getType());
|
||||
// 修改状态
|
||||
@@ -229,7 +229,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 添加日志参数
|
||||
OperatorLogs.add(OperatorLogs.NAME, record.getName());
|
||||
// 从缓存中查询
|
||||
List<SystemMenuCacheDTO> cache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> cache = userPermissionService.getMenuCache();
|
||||
// 获取要删除的id
|
||||
List<Long> deletedIdList = this.getChildrenIdList(id, cache, record.getType());
|
||||
// 删除菜单
|
||||
@@ -239,7 +239,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 删除菜单缓存
|
||||
cache.removeIf(s -> deletedIdList.contains(s.getId()));
|
||||
// 删除引用缓存
|
||||
permissionService.getRoleMenuCache()
|
||||
userPermissionService.getRoleMenuCache()
|
||||
.values()
|
||||
.forEach(roleMenus -> roleMenus.removeIf(s -> deletedIdList.contains(s.getId())));
|
||||
log.info("SystemMenuService-deleteSystemMenu deletedIdList: {}, effect: {}", deletedIdList, effect);
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.request.menu.SystemRoleGrantMenuRequest;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemRoleMenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -49,7 +49,7 @@ public class SystemRoleMenuServiceImpl implements SystemRoleMenuService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -104,7 +104,7 @@ public class SystemRoleMenuServiceImpl implements SystemRoleMenuService {
|
||||
effect += insertMenuIdList.size();
|
||||
}
|
||||
// 更新缓存
|
||||
Map<Long, List<SystemMenuCacheDTO>> cache = permissionService.getRoleMenuCache();
|
||||
Map<Long, List<SystemMenuCacheDTO>> cache = userPermissionService.getRoleMenuCache();
|
||||
List<SystemMenuCacheDTO> roleCache = cache.computeIfAbsent(roleId, s -> new ArrayList<>());
|
||||
roleCache.clear();
|
||||
roleCache.addAll(SystemMenuConvert.MAPPER.toCache(menuList));
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.orion.visor.module.infra.entity.request.role.SystemRoleUpdateRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemRoleVO;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.DataPermissionService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemRoleService;
|
||||
import com.orion.visor.module.infra.service.SystemUserRoleService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -51,7 +51,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Resource
|
||||
private SystemUserRoleService systemUserRoleService;
|
||||
@@ -72,7 +72,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.insert(record);
|
||||
log.info("SystemRoleService-createSystemRole effect: {}, domain: {}", effect, JSON.toJSONString(record));
|
||||
// 设置到缓存
|
||||
permissionService.getRoleCache().put(record.getId(), record);
|
||||
userPermissionService.getRoleCache().put(record.getId(), record);
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.updateById(updateRecord);
|
||||
log.info("SystemRoleService-updateSystemRoleById effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||
// 设置到缓存
|
||||
SystemRoleDO roleCache = permissionService.getRoleCache().get(id);
|
||||
SystemRoleDO roleCache = userPermissionService.getRoleCache().get(id);
|
||||
roleCache.setName(updateRecord.getName());
|
||||
return effect;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.updateById(updateRecord);
|
||||
log.info("SystemRoleService-updateRoleStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||
// 修改本地缓存状态
|
||||
SystemRoleDO roleCache = permissionService.getRoleCache().get(id);
|
||||
SystemRoleDO roleCache = userPermissionService.getRoleCache().get(id);
|
||||
roleCache.setStatus(status);
|
||||
// 删除数据权限缓存
|
||||
dataPermissionService.clearRoleCache(id);
|
||||
@@ -180,9 +180,9 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
// 删除角色菜单关联
|
||||
effect += systemRoleMenuDAO.deleteByRoleId(id);
|
||||
// 删除角色缓存
|
||||
permissionService.getRoleCache().remove(id);
|
||||
userPermissionService.getRoleCache().remove(id);
|
||||
// 删除菜单缓存
|
||||
permissionService.getRoleMenuCache().remove(id);
|
||||
userPermissionService.getRoleMenuCache().remove(id);
|
||||
// 删除用户缓存中的角色
|
||||
systemUserRoleService.deleteUserCacheRoleAsync(id, userIdList);
|
||||
// 删除数据权限缓存
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.orion.visor.module.infra.dao.OperatorLogDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserRoleDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.define.cache.TipsCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.cache.UserCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.config.AppAuthenticationConfig;
|
||||
@@ -302,11 +301,6 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long userId) {
|
||||
return systemRoleDAO.getRoleIdByUserIdAndRoleCode(userId, RoleDefine.ADMIN_CODE) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户名否存在
|
||||
*
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.convert.SystemMenuConvert;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemMenuDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleMenuDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemMenuDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserCollectInfoVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.PreferenceTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.PreferenceService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import com.orion.visor.module.infra.service.TipsService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 用户权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserPermissionServiceImpl implements UserPermissionService {
|
||||
|
||||
@Getter
|
||||
private final Map<Long, SystemRoleDO> roleCache = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private final List<SystemMenuCacheDTO> menuCache = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final Map<Long, List<SystemMenuCacheDTO>> roleMenuCache = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private SystemRoleDAO systemRoleDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuDAO systemMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuService systemMenuService;
|
||||
|
||||
@Resource
|
||||
private PreferenceService preferenceService;
|
||||
|
||||
@Resource
|
||||
private TipsService tipsService;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void initPermissionCache() {
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("initPermissionCache-start");
|
||||
roleCache.clear();
|
||||
menuCache.clear();
|
||||
roleMenuCache.clear();
|
||||
// 加载所有角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectList(null);
|
||||
for (SystemRoleDO role : roles) {
|
||||
roleCache.put(role.getId(), role);
|
||||
}
|
||||
// 加载所有菜单信息
|
||||
List<SystemMenuDO> menuList = systemMenuDAO.selectList(null);
|
||||
List<SystemMenuCacheDTO> menus = SystemMenuConvert.MAPPER.toCache(menuList);
|
||||
Map<Long, SystemMenuCacheDTO> menuMapping = menus.stream()
|
||||
.collect(Collectors.toMap(SystemMenuCacheDTO::getId, Function.identity()));
|
||||
menuCache.addAll(menus);
|
||||
// 查询所有角色菜单
|
||||
systemRoleMenuDAO.selectList(null)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SystemRoleMenuDO::getRoleId,
|
||||
Collectors.mapping(SystemRoleMenuDO::getMenuId, Collectors.toList())))
|
||||
.forEach((roleId, menuIdList) -> {
|
||||
// 获取菜单引用
|
||||
List<SystemMenuCacheDTO> roleMenus = menuIdList.stream()
|
||||
.map(menuMapping::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// 获取角色引用
|
||||
roleMenuCache.put(roleId, roleMenus);
|
||||
});
|
||||
log.info("initPermissionCache-end used: {}ms", System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员或包含此角色
|
||||
return RoleDefine.containsAdmin(roles.values()) || roles.containsValue(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
if (Arrays1.isEmpty(roles)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> enableRoles = this.getUserEnabledRoles();
|
||||
if (enableRoles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员 || 有此角色
|
||||
return RoleDefine.containsAdmin(enableRoles.values())
|
||||
|| Arrays.stream(roles).anyMatch(enableRoles::containsValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查普通角色是否有此权限
|
||||
return roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
if (Arrays1.isEmpty(permissions)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查用户角色是否包含权限
|
||||
return Arrays.stream(permissions)
|
||||
.anyMatch(perm -> roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, perm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return Lists.empty();
|
||||
}
|
||||
// 查询角色菜单
|
||||
Stream<SystemMenuCacheDTO> mergeStream;
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部菜单
|
||||
mergeStream = menuCache.stream();
|
||||
} else {
|
||||
// 当前用户所适配的角色菜单
|
||||
mergeStream = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct();
|
||||
}
|
||||
// 状态过滤
|
||||
List<SystemMenuVO> menus = mergeStream
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.filter(s -> !MenuTypeEnum.FUNCTION.getType().equals(s.getType()))
|
||||
.map(SystemMenuConvert.MAPPER::to)
|
||||
.collect(Collectors.toList());
|
||||
// 构建菜单树
|
||||
return systemMenuService.buildSystemMenuTree(menus);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public UserPermissionVO getUserPermission() {
|
||||
// 获取用户信息
|
||||
UserCollectInfoVO user = SystemUserConvert.MAPPER.toCollectInfo(SecurityUtils.getLoginUser());
|
||||
Long id = user.getId();
|
||||
// 获取用户系统偏好
|
||||
Future<Map<String, Object>> systemPreference = preferenceService.getPreferenceAsync(id, PreferenceTypeEnum.SYSTEM);
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
// 获取用户权限
|
||||
List<String> permissions;
|
||||
if (roles.isEmpty()) {
|
||||
permissions = Lists.empty();
|
||||
} else {
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部权限
|
||||
permissions = Lists.of(Const.ASTERISK);
|
||||
} else {
|
||||
// 当前用户所适配的角色的权限
|
||||
permissions = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
// 设置已提示的 key
|
||||
user.setTippedKeys(tipsService.getTippedKeys());
|
||||
// 获取异步结果
|
||||
user.setSystemPreference(systemPreference.get());
|
||||
// 组装数据
|
||||
return UserPermissionVO.builder()
|
||||
.user(user)
|
||||
.roles(roles.values())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查角色是否有权限
|
||||
*
|
||||
* @param roleId roleId
|
||||
* @param permission permission
|
||||
* @return 是否有权限
|
||||
*/
|
||||
private boolean checkRoleHasPermission(Long roleId, String permission) {
|
||||
// 获取角色权限列表
|
||||
List<SystemMenuCacheDTO> menus = roleMenuCache.get(roleId);
|
||||
if (Lists.isEmpty(menus)) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否有此权限
|
||||
return menus.stream()
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(permission::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户启用的角色
|
||||
*
|
||||
* @return roles
|
||||
*/
|
||||
private Map<Long, String> getUserEnabledRoles() {
|
||||
// 获取当前用户角色
|
||||
List<UserRole> userRoles = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getRoles)
|
||||
.orElse(Lists.empty());
|
||||
if (Lists.isEmpty(userRoles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
// 获取角色编码
|
||||
Map<Long, String> roles = userRoles.stream()
|
||||
.map(UserRole::getId)
|
||||
.map(roleCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
// 过滤未启用的角色
|
||||
.filter(r -> RoleStatusEnum.ENABLED.getStatus().equals(r.getStatus()))
|
||||
.collect(Collectors.toMap(SystemRoleDO::getId, SystemRoleDO::getCode));
|
||||
if (Maps.isEmpty(roles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,11 @@
|
||||
"type": "java.lang.Integer",
|
||||
"description": "凭证续签最大次数."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedSendThreshold",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "登录失败发送站内信阈值."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedLockCount",
|
||||
"type": "java.lang.Integer",
|
||||
|
||||
@@ -24,12 +24,42 @@
|
||||
SELECT role_id
|
||||
FROM system_user_role
|
||||
WHERE user_id = #{userId}
|
||||
AND deleted = 0
|
||||
AND role_id IN (SELECT id FROM system_role WHERE CODE = #{code} AND deleted = 0) LIMIT 1
|
||||
AND deleted = 0
|
||||
AND role_id IN
|
||||
(SELECT id
|
||||
FROM system_role
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND code IN
|
||||
<foreach collection="codeList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
)
|
||||
</select>
|
||||
|
||||
<select id="getPermissionByRoleIdAndPermission" resultType="java.lang.String">
|
||||
SELECT m.permission
|
||||
FROM system_menu m
|
||||
LEFT JOIN system_role_menu rm ON rm.menu_id = m.id
|
||||
WHERE rm.deleted = 0
|
||||
AND m.deleted = 0
|
||||
AND m.type = 3
|
||||
AND m.status = 1
|
||||
<if test="permissionList != null and permissionList.size() > 0">
|
||||
AND m.permission IN
|
||||
<foreach collection="permissionList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
AND rm.role_id IN
|
||||
<foreach collection="roleIdList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="selectRoleByUserId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM system_role
|
||||
WHERE deleted = 0
|
||||
AND id IN (SELECT role_id FROM system_user_role WHERE user_id = #{userId} AND deleted = 0)
|
||||
|
||||
Reference in New Issue
Block a user