越权系统框架整改
This commit is contained in:
parent
d3dc26b1ea
commit
24a1021348
|
|
@ -4,6 +4,7 @@ import java.util.List;
|
|||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.bonus.common.annotation.RequiresPermissions;
|
||||
import com.bonus.common.annotation.SysLog;
|
||||
import com.bonus.common.enums.OperaType;
|
||||
import com.bonus.common.utils.encryption.Sm4Utils;
|
||||
|
|
@ -60,7 +61,8 @@ public class SysUserController extends BaseController
|
|||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:user:list')")
|
||||
@RequiresPermissions("system:user:list")
|
||||
@GetMapping("/list")
|
||||
@SysLog(title = "用户管理", businessType = OperaType.QUERY, logType = 0, module = "系统管理->用户管理", details = "查询用户列表")
|
||||
public TableDataInfo list(SysUser user)
|
||||
|
|
@ -253,7 +255,8 @@ public class SysUserController extends BaseController
|
|||
/**
|
||||
* 获取部门树列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:list')")
|
||||
// @PreAuthorize("@ss.hasPermi('system:user:list')")
|
||||
@RequiresPermissions("system:user:list")
|
||||
@GetMapping("/deptTree")
|
||||
public AjaxResult deptTree(SysDept dept)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.bonus.common.annotation;
|
||||
|
||||
/**
|
||||
* 权限注解的验证模式
|
||||
*
|
||||
* @author bonus
|
||||
*
|
||||
*/
|
||||
public enum Logical
|
||||
{
|
||||
/**
|
||||
* 必须具有所有的元素
|
||||
*/
|
||||
AND,
|
||||
|
||||
/**
|
||||
* 只需具有其中一个元素
|
||||
*/
|
||||
OR
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.bonus.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 登录认证:只有登录之后才能进入该方法
|
||||
*
|
||||
* @author bonus
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface RequiresLogin
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.bonus.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 权限认证:必须具有指定权限才能进入该方法
|
||||
*
|
||||
* @author bonus
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
public @interface RequiresPermissions
|
||||
{
|
||||
/**
|
||||
* 需要校验的权限码
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 验证模式:AND | OR,默认AND
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.bonus.common.context;
|
||||
|
||||
/**
|
||||
* @className:PermissionContextHolder
|
||||
* @author:cwchen
|
||||
* @date:2025-09-09-10:43
|
||||
* @version:1.0
|
||||
* @description:权限上下文持有类
|
||||
*/
|
||||
public class PermissionContextHolder {
|
||||
|
||||
private static final ThreadLocal<String> PERMISSION_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void setPermission(String permission) {
|
||||
PERMISSION_CONTEXT.set(permission);
|
||||
}
|
||||
|
||||
public static String getPermission() {
|
||||
return PERMISSION_CONTEXT.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
PERMISSION_CONTEXT.remove();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.bonus.common.exception;
|
||||
|
||||
/**
|
||||
* 未能通过的登录认证异常
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
public class NotLoginException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotLoginException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.bonus.common.exception;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 未能通过的权限认证异常
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
public class NotPermissionException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotPermissionException(String permission)
|
||||
{
|
||||
super(permission);
|
||||
}
|
||||
|
||||
public NotPermissionException(String[] permissions)
|
||||
{
|
||||
super(StringUtils.join(permissions, ","));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package com.bonus.framework.aspectj;
|
||||
|
||||
import com.bonus.common.annotation.RequiresPermissions;
|
||||
import com.bonus.framework.auth.AuthLogic;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 基于 Spring Aop 的注解鉴权
|
||||
*
|
||||
* @author kong
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class PreAuthorizeAspect
|
||||
{
|
||||
|
||||
@Autowired
|
||||
AuthLogic authLogic;
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
public PreAuthorizeAspect()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义AOP签名 (切入所有使用鉴权注解的方法)
|
||||
*/
|
||||
public static final String POINTCUT_SIGN = "@annotation(com.bonus.common.annotation.RequiresPermissions)";
|
||||
|
||||
/**
|
||||
* 声明AOP签名
|
||||
*/
|
||||
@Pointcut(POINTCUT_SIGN)
|
||||
public void pointcut()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕切入
|
||||
*
|
||||
* @param joinPoint 切面对象
|
||||
* @return 底层方法执行后的返回值
|
||||
* @throws Throwable 底层方法抛出的异常
|
||||
*/
|
||||
@Around("pointcut()")
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable
|
||||
{
|
||||
// 注解鉴权
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
checkMethodAnnotation(signature.getMethod(),joinPoint);
|
||||
try
|
||||
{
|
||||
// 执行原有逻辑
|
||||
Object obj = joinPoint.proceed();
|
||||
return obj;
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对一个Method对象进行注解检查
|
||||
*/
|
||||
public void checkMethodAnnotation(Method method,ProceedingJoinPoint joinPoint)
|
||||
{
|
||||
|
||||
// 校验 @RequiresPermissions 注解
|
||||
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
|
||||
if (requiresPermissions != null)
|
||||
{
|
||||
authLogic.checkPermi(requiresPermissions,joinPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
package com.bonus.framework.auth;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.bonus.common.annotation.Logical;
|
||||
import com.bonus.common.annotation.RequiresLogin;
|
||||
import com.bonus.common.annotation.RequiresPermissions;
|
||||
import com.bonus.common.context.PermissionContextHolder;
|
||||
import com.bonus.common.core.domain.model.LoginUser;
|
||||
import com.bonus.common.exception.NotLoginException;
|
||||
import com.bonus.common.utils.DateUtils;
|
||||
import com.bonus.common.utils.SecurityUtils;
|
||||
import com.bonus.common.utils.StringUtils;
|
||||
import com.bonus.common.utils.ip.IpUtils;
|
||||
import com.bonus.framework.manager.AsyncManager;
|
||||
import com.bonus.framework.manager.factory.AsyncFactory;
|
||||
import com.bonus.framework.web.service.TokenService;
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* @className:AuthLogic
|
||||
* @author:cwchen
|
||||
* @date:2025-09-08-17:54
|
||||
* @version:1.0
|
||||
* @description:鉴权实现类
|
||||
*/
|
||||
@Component
|
||||
public class AuthLogic {
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* 所有权限标识
|
||||
*/
|
||||
private static final String ALL_PERMISSION = "*:*:*";
|
||||
|
||||
/**
|
||||
* 管理员角色权限标识
|
||||
*/
|
||||
private static final String SUPER_ADMIN = "admin";
|
||||
|
||||
/**
|
||||
* 管理员角色权限标识
|
||||
*/
|
||||
private static final String COMPANY_ADMIN = "company_admin";
|
||||
|
||||
/**
|
||||
* 检验用户是否已经登录,如未登录,则抛出异常
|
||||
*/
|
||||
public void checkLogin() {
|
||||
getLoginUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常
|
||||
*
|
||||
* @return 用户缓存信息
|
||||
*/
|
||||
public LoginUser getLoginUser() {
|
||||
String token = tokenService.getToken();
|
||||
if (token == null) {
|
||||
throw new NotLoginException("未提供token");
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new NotLoginException("无效的token");
|
||||
}
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常
|
||||
*
|
||||
* @param token 前端传递的认证信息
|
||||
* @return 用户缓存信息
|
||||
*/
|
||||
public LoginUser getLoginUser(String token) {
|
||||
return tokenService.getLoginUser(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermi(String permission) {
|
||||
return hasPermi(getPermiList(), permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param requiresPermissions 注解对象
|
||||
*/
|
||||
public void checkPermi(RequiresPermissions requiresPermissions, ProceedingJoinPoint joinPoint) {
|
||||
// 获取请求参数
|
||||
boolean needPermission = true;
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
for (Object obj : args) {
|
||||
if (obj instanceof JSONObject) {
|
||||
JSONObject jsonObject = (JSONObject) obj;
|
||||
if ("1".equals(jsonObject.getStr("skipPermission"))) {
|
||||
needPermission = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needPermission) {
|
||||
// 使用自定义的PermissionContextHolder替代SecurityContextHolder
|
||||
PermissionContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
|
||||
try {
|
||||
if (requiresPermissions.logical() == Logical.AND) {
|
||||
checkPermiAnd(requiresPermissions.value());
|
||||
} else {
|
||||
checkPermiOr(requiresPermissions.value());
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
// 记录越权日志(异步处理,不阻塞当前线程)
|
||||
addErrorLogsAsync(joinPoint, requiresPermissions, e.getMessage());
|
||||
throw e;
|
||||
} finally {
|
||||
// 清理ThreadLocal,防止内存泄漏
|
||||
PermissionContextHolder.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步记录越权日志(不阻塞当前线程)
|
||||
*/
|
||||
private void addErrorLogsAsync(ProceedingJoinPoint joinPoint, RequiresPermissions requiresPermissions, String errorMessage) {
|
||||
try {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
loginUser.setIpaddr(IpUtils.getIpAddr());
|
||||
SysLogsVo vo = SysLogsVo.getExceedAuthorithSysLogsVo(loginUser, joinPoint);
|
||||
// 假设LogsUtils.setRequestValue存在,如果不存在需要处理
|
||||
LogsUtils.setRequestValue(joinPoint, vo, null);
|
||||
|
||||
// 设置基础信息
|
||||
vo.setParams(StringUtils.join(requiresPermissions.value(), ","));
|
||||
vo.setOperTime(DateUtils.getTime());
|
||||
vo.setTimes("0");
|
||||
|
||||
// 异步查询日志模块信息并记录日志
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
SysLogsVo queryVo = new SysLogsVo();
|
||||
queryVo.setParams(requiresPermissions.value()[0]);
|
||||
|
||||
// 异步查询日志模块信息
|
||||
Future<Map<String, String>> mapFuture = AsyncManager.me().executeGetLogsModule(queryVo);
|
||||
Map<String, String> result = mapFuture.get(); // 这里在异步线程中阻塞是可以的
|
||||
|
||||
// 设置查询结果
|
||||
vo.setModel(result.get("module"));
|
||||
vo.setTitle(result.get("title"));
|
||||
vo.setOperateDetail(result.get("detail"));
|
||||
vo.setOperType(result.get("bussType"));
|
||||
vo.setResultData(result.get("resultData"));
|
||||
|
||||
// 记录日志
|
||||
AsyncManager.me().execute(AsyncFactory.addSysOperLog(vo));
|
||||
} catch (Exception e) {
|
||||
System.err.println("越权日志记录失败: " + e.getMessage());
|
||||
// 即使查询失败也记录基础日志信息
|
||||
vo.setTitle("权限验证失败");
|
||||
vo.setOperateDetail("查询操作模块信息失败");
|
||||
AsyncManager.me().execute(AsyncFactory.addSysOperLog(vo));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
System.err.println("越权日志记录初始化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,必须全部拥有
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
*/
|
||||
public void checkPermiAnd(String... permissions) {
|
||||
Set<String> permissionList = getPermiList();
|
||||
|
||||
for (String permission : permissions) {
|
||||
System.err.println(hasPermi(permissionList, permission));
|
||||
if (!hasPermi(permissionList, permission)) {
|
||||
// throw new NotPermissionException(permission);
|
||||
throw new AccessDeniedException(permission+":越权访问");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,只需包含其中一个
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public void checkPermiOr(String... permissions) {
|
||||
Set<String> permissionList = getPermiList();
|
||||
for (String permission : permissions) {
|
||||
if (hasPermi(permissionList, permission)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (permissions.length > 0) {
|
||||
// throw new NotPermissionException(permissions);
|
||||
throw new AccessDeniedException("越权访问");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresLogin)鉴权
|
||||
*
|
||||
* @param at 注解对象
|
||||
*/
|
||||
public void checkByAnnotation(RequiresLogin at) {
|
||||
this.checkLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresPermissions)鉴权
|
||||
*
|
||||
* @param at 注解对象
|
||||
*/
|
||||
public void checkByAnnotation(RequiresPermissions at) {
|
||||
String[] permissionArray = at.value();
|
||||
if (at.logical() == Logical.AND) {
|
||||
this.checkPermiAnd(permissionArray);
|
||||
} else {
|
||||
this.checkPermiOr(permissionArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前账号的权限列表
|
||||
*
|
||||
* @return 权限列表
|
||||
*/
|
||||
public Set<String> getPermiList() {
|
||||
try {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
return loginUser.getPermissions();
|
||||
} catch (Exception e) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param authorities 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermi(Collection<String> authorities, String permission) {
|
||||
return authorities.stream().filter(StringUtils::hasText)
|
||||
.anyMatch(x -> ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.bonus.framework.auth;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bonus.common.utils.ServletUtils;
|
||||
import com.bonus.common.utils.StringUtils;
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
public class LogsUtils {
|
||||
/**
|
||||
* 获取请求的参数,放到log中
|
||||
*
|
||||
* @param operLog 操作日志
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static void setRequestValue(ProceedingJoinPoint joinPoint, SysLogsVo operLog, String[] excludeParamNames) throws Exception {
|
||||
String requestMethod = operLog.getMethod();
|
||||
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
|
||||
boolean bResult = HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod);
|
||||
if (StringUtils.isEmpty(paramsMap) && bResult)
|
||||
{
|
||||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||
operLog.setParams(StringUtils.substring(params, 0, 2000));
|
||||
}
|
||||
else {
|
||||
operLog.setParams(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参数拼装
|
||||
*/
|
||||
private static String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
|
||||
{
|
||||
String params = "";
|
||||
if (paramsArray != null && paramsArray.length > 0)
|
||||
{
|
||||
for (Object o : paramsArray)
|
||||
{
|
||||
if (StringUtils.isNotNull(o) && !isFilterObject(o))
|
||||
{
|
||||
try
|
||||
{
|
||||
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
|
||||
params += jsonObj.toString() + " ";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params.trim();
|
||||
}
|
||||
|
||||
/** 排除敏感属性字段 */
|
||||
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
|
||||
/**
|
||||
* 忽略敏感属性
|
||||
*/
|
||||
public static PropertyPreExcludeFilter2 excludePropertyPreFilter(String[] excludeParamNames)
|
||||
{
|
||||
return new PropertyPreExcludeFilter2().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的对象。
|
||||
*
|
||||
* @param o 对象信息。
|
||||
* @return 如果是需要过滤的对象,则返回true;否则返回false。
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static boolean isFilterObject(final Object o)
|
||||
{
|
||||
Class<?> clazz = o.getClass();
|
||||
if (clazz.isArray())
|
||||
{
|
||||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(clazz))
|
||||
{
|
||||
Collection collection = (Collection) o;
|
||||
for (Object value : collection)
|
||||
{
|
||||
return value instanceof MultipartFile;
|
||||
}
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(clazz))
|
||||
{
|
||||
Map map = (Map) o;
|
||||
for (Object value : map.entrySet())
|
||||
{
|
||||
Map.Entry entry = (Map.Entry) value;
|
||||
return entry.getValue() instanceof MultipartFile;
|
||||
}
|
||||
}
|
||||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||
|| o instanceof BindingResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.bonus.framework.auth;
|
||||
|
||||
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
|
||||
|
||||
/**
|
||||
* 排除JSON敏感属性
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
public class PropertyPreExcludeFilter2 extends SimplePropertyPreFilter
|
||||
{
|
||||
public PropertyPreExcludeFilter2()
|
||||
{
|
||||
}
|
||||
|
||||
public PropertyPreExcludeFilter2 addExcludes(String... filters)
|
||||
{
|
||||
for (int i = 0; i < filters.length; i++)
|
||||
{
|
||||
this.getExcludes().add(filters[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
package com.bonus.framework.manager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import com.bonus.common.utils.Threads;
|
||||
import com.bonus.common.utils.spring.SpringUtils;
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import com.bonus.system.service.ISysOperLogService;
|
||||
|
||||
/**
|
||||
* 异步任务管理器
|
||||
|
|
@ -45,6 +48,48 @@ public class AsyncManager
|
|||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行带返回值的任务
|
||||
*
|
||||
* @param task 带返回值的任务
|
||||
* @param <T> 返回值类型
|
||||
* @return Future对象,用于获取执行结果
|
||||
*/
|
||||
public <T> Future<T> execute(Callable<T> task)
|
||||
{
|
||||
return executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行FutureTask任务
|
||||
*
|
||||
* @param futureTask FutureTask任务
|
||||
* @param <T> 返回值类型
|
||||
* @return Future对象,用于获取执行结果
|
||||
*/
|
||||
public <T> Future<T> execute(FutureTask<T> futureTask)
|
||||
{
|
||||
executor.schedule(futureTask, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询日志模块的异步任务
|
||||
*
|
||||
* @param sysLogsVo 操作日志信息
|
||||
* @return Future对象,用于获取查询结果
|
||||
*/
|
||||
public Future<Map<String, String>> executeGetLogsModule(final SysLogsVo sysLogsVo)
|
||||
{
|
||||
Callable<Map<String, String>> callable = new Callable<Map<String, String>>() {
|
||||
@Override
|
||||
public Map<String, String> call() throws Exception {
|
||||
return SpringUtils.getBean(ISysOperLogService.class).getLogsModule(sysLogsVo);
|
||||
}
|
||||
};
|
||||
return execute(callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止任务线程池
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.bonus.framework.manager.factory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -121,4 +124,23 @@ public class AsyncFactory
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询操作的日志模块
|
||||
*
|
||||
* @param sysLogsVo 操作日志信息
|
||||
* @return 任务task
|
||||
*/
|
||||
public static FutureTask<Map<String,String>> getLogsModule(final SysLogsVo sysLogsVo)
|
||||
{
|
||||
Callable<Map<String,String>> callable = new Callable<Map<String,String>>() {
|
||||
@Override
|
||||
public Map<String,String> call() throws Exception {
|
||||
return SpringUtils.getBean(ISysOperLogService.class).getLogsModule(sysLogsVo);
|
||||
}
|
||||
};
|
||||
return new FutureTask<>(callable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,4 +236,31 @@ public class TokenService
|
|||
{
|
||||
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*/
|
||||
public String getToken()
|
||||
{
|
||||
return getToken(ServletUtils.getRequest());
|
||||
}
|
||||
|
||||
public LoginUser getLoginUser(String token){
|
||||
if (StringUtils.isNotEmpty(token)){
|
||||
try
|
||||
{
|
||||
Claims claims = parseToken(token);
|
||||
// 解析对应的权限以及用户信息
|
||||
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
|
||||
String userKey = getTokenKey(uuid);
|
||||
LoginUser user = redisCache.getCacheObject(userKey);
|
||||
return user;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取用户信息异常'{}'", e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.bonus.system.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 日志菜单头
|
||||
* @author weiweiw
|
||||
*/
|
||||
@Data
|
||||
public class SysLogsMenuHead {
|
||||
|
||||
|
||||
public String menuName;
|
||||
|
||||
private String menuType;
|
||||
|
||||
public String menuName2;
|
||||
|
||||
public String menuName3;
|
||||
|
||||
public String menuName4;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.bonus.system.mapper;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import com.bonus.system.domain.SysLogsMenuHead;
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import com.bonus.system.domain.SysOperLog;
|
||||
|
||||
|
|
@ -65,4 +66,13 @@ public interface SysOperLogMapper
|
|||
* @date 2025/9/5 13:41
|
||||
*/
|
||||
SysLogsVo getModule(String operUri);
|
||||
|
||||
/**
|
||||
* 查询 异常日志路径
|
||||
* @param sysLogsVo
|
||||
* @return List<SysLogsMenuHead>
|
||||
* @author cwchen
|
||||
* @date 2025/9/9 9:55
|
||||
*/
|
||||
List<SysLogsMenuHead> getLogsErrorModule(SysLogsVo sysLogsVo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.bonus.system.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import com.bonus.system.domain.SysOperLog;
|
||||
|
|
@ -56,4 +57,13 @@ public interface ISysOperLogService
|
|||
* @date 2025/9/5 13:14
|
||||
*/
|
||||
void addSysOperLog(SysLogsVo sysLogsVo);
|
||||
|
||||
/**
|
||||
* 查询操作模块
|
||||
* @param sysLogsVo
|
||||
* @return Map<String,String>
|
||||
* @author cwchen
|
||||
* @date 2025/9/9 9:51
|
||||
*/
|
||||
Map<String, String> getLogsModule(SysLogsVo sysLogsVo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
package com.bonus.system.service.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.bonus.common.utils.global.SystemGlobal;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bonus.common.enums.OperaType;
|
||||
import com.bonus.system.domain.SysLogsMenuHead;
|
||||
import com.bonus.system.domain.SysLogsVo;
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -26,6 +32,8 @@ public class SysOperLogServiceImpl implements ISysOperLogService
|
|||
@Autowired
|
||||
private SysOperLogMapper operLogMapper;
|
||||
|
||||
public static final String MENU_TYPE_FILE = "F";
|
||||
|
||||
/**
|
||||
* 新增操作日志
|
||||
*
|
||||
|
|
@ -103,4 +111,77 @@ public class SysOperLogServiceImpl implements ISysOperLogService
|
|||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getLogsModule(SysLogsVo sysLogsVo) {
|
||||
Map<String, String> maps= Maps.newHashMap();
|
||||
Map result=Maps.newHashMap();
|
||||
result.put("code","403");
|
||||
result.put("data","");
|
||||
result.put("msg","数据接口未授权");
|
||||
try{
|
||||
List<SysLogsMenuHead> list =operLogMapper.getLogsErrorModule(sysLogsVo);
|
||||
if(CollectionUtils.isNotEmpty(list)){
|
||||
SysLogsMenuHead vo=list.get(0);
|
||||
StringBuffer sb=new StringBuffer();
|
||||
String type=vo.getMenuType();
|
||||
if(StringUtils.isNotEmpty(vo.getMenuName4())){
|
||||
sb.append(vo.getMenuName4());
|
||||
}
|
||||
if(StringUtils.isNotEmpty(vo.getMenuName3())){
|
||||
sbAppend(sb);
|
||||
sb.append(vo.getMenuName3());
|
||||
}
|
||||
if(StringUtils.isNotEmpty(vo.getMenuName2())){
|
||||
sbAppend(sb);
|
||||
sb.append(vo.getMenuName2());
|
||||
}
|
||||
if( MENU_TYPE_FILE .equals(type)){
|
||||
if(vo.getMenuName().contains(OperaType.INSERT)){
|
||||
maps.put("bussType",OperaType.INSERT);
|
||||
}else if(vo.getMenuName().contains(OperaType.UPDATE)){
|
||||
maps.put("bussType",OperaType.UPDATE);
|
||||
}else if(vo.getMenuName().contains(OperaType.DELETE)){
|
||||
maps.put("bussType",OperaType.DELETE);
|
||||
}else if(vo.getMenuName().contains(OperaType.IMPORT)){
|
||||
maps.put("bussType",OperaType.IMPORT);
|
||||
}else if(vo.getMenuName().contains(OperaType.DOWNLOAD)){
|
||||
maps.put("bussType",OperaType.DOWNLOAD);
|
||||
}else if(vo.getMenuName().contains(OperaType.EXPORT)){
|
||||
maps.put("bussType",OperaType.EXPORT);
|
||||
}else if(vo.getMenuName().contains(OperaType.FLASH)){
|
||||
maps.put("bussType",OperaType.FLASH);
|
||||
}else if(vo.getMenuName().contains(OperaType.GRANT)){
|
||||
maps.put("bussType",OperaType.GRANT);
|
||||
} else {
|
||||
maps.put("bussType",OperaType.OTHER);
|
||||
}
|
||||
maps.put("title",vo.getMenuName2());
|
||||
}else{
|
||||
maps.put("bussType",OperaType.QUERY);
|
||||
sbAppend(sb);
|
||||
sb.append(vo.getMenuName());
|
||||
maps.put("title",vo.getMenuName());
|
||||
}
|
||||
maps.put("detail", "进行数据的"+maps.get("bussType"));
|
||||
maps.put("module",sb.toString());
|
||||
maps.put("resultData", JSON.toJSONString(result));
|
||||
}
|
||||
|
||||
}catch (Exception e){
|
||||
maps.put("resultData", JSON.toJSONString(result));
|
||||
maps.put("title","智慧图档");
|
||||
maps.put("module","智慧图档->数据接口");
|
||||
maps.put("detail","进行数据访问");
|
||||
maps.put("bussType", "未知");
|
||||
log.error(e.toString(),e);
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
public void sbAppend(StringBuffer sb){
|
||||
if(StringUtils.isNotEmpty(sb.toString())){
|
||||
sb.append("->");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,8 +106,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
where sm3.component=#{module}
|
||||
limit 1
|
||||
</select>
|
||||
<!--查询 异常日志路径-->
|
||||
<select id="getLogsErrorModule" resultType="com.bonus.system.domain.SysLogsMenuHead">
|
||||
select sm.menu_name menuName,sm.menu_type menuType,sm2.menu_name menuName2,
|
||||
sm3.menu_name menuName3,sm4.menu_name menuName4
|
||||
FROM da_ky_sys_menu sm
|
||||
left join da_ky_sys_menu sm2 on sm.parent_id=sm2.menu_id and sm2.visible=0
|
||||
left join da_ky_sys_menu sm3 on sm2.parent_id=sm3.menu_id and sm3.visible=0
|
||||
left join da_ky_sys_menu sm4 on sm3.parent_id=sm4.menu_id and sm4.visible=0
|
||||
where sm.perms=#{params}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<update id="cleanOperLog">
|
||||
<update id="cleanOperLog">
|
||||
truncate table da_ky_sys_oper_log
|
||||
</update>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue