提交 787c0c88 作者: 沈振路

操作日志 + 操作权限

上级 7a3252db
/.idea/
/target/
/customer-service/
/test-customer-service/
package com.yaoyaozw.customer.annotations;
import com.yaoyaozw.customer.enums.AccountParamType;
import com.yaoyaozw.customer.enums.AccountTableColumnType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author darker
* @date 2022/12/13 12:04
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccountOperateControl {
AccountParamType paramType();
// 校验参数名
String paramName();
// 校验参数类型, 是appId 还是 公众号主键id
AccountTableColumnType columnType() default AccountTableColumnType.AUTH_ID;
// property是 paramType 为 REQUEST_BODY 时, 用于指定参数实体类中公众号校验字段的字段名
String property() default "authId";
// table 和 column 是当 paramType 为 TABLE_PRIMARY 时需要的参数 table指定主键对应的表名,column指定该表中的公众号字段名
String table() default "";
String column() default "auth_id";
}
package com.yaoyaozw.customer.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author darker
* @date 2022/12/9 11:36
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperateLog {
String module() default "";
String desc() default "";
}
package com.yaoyaozw.customer.aop;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yaoyaozw.customer.annotations.AccountOperateControl;
import com.yaoyaozw.customer.common.BaseResult;
import com.yaoyaozw.customer.components.TokenManager;
import com.yaoyaozw.customer.constants.CustomerCommonConstant;
import com.yaoyaozw.customer.entity.AccountSetup;
import com.yaoyaozw.customer.enums.AccountParamType;
import com.yaoyaozw.customer.enums.AccountTableColumnType;
import com.yaoyaozw.customer.exception.AopCommonException;
import com.yaoyaozw.customer.mapper.MaterialAopCommonMapper;
import com.yaoyaozw.customer.service.AccountSetupService;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author darker
* @date 2022/12/13 12:11
*/
@Component
@Aspect
public class AccountOperateControlAop {
private final static Logger localLog = LoggerFactory.getLogger(AccountOperateControlAop.class);
@Autowired
private TokenManager tokenManager;
@Autowired
private AccountSetupService accountSetupService;
@Autowired
private MaterialAopCommonMapper aopCommonMapper;
@Pointcut("@annotation(com.yaoyaozw.customer.annotations.AccountOperateControl)")
public void operateControlCut() {
//公众号权限控制切点
}
@Before("operateControlCut()")
public void operateControl(JoinPoint joinPoint) throws Throwable {
// 获取注解的详细信息
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method declaredMethod = methodSignature.getMethod();
AccountOperateControl operateControl = declaredMethod.getAnnotation(AccountOperateControl.class);
// 获取校验字段值
String paramName = operateControl.paramName();
int index = 0;
for (String parameterName : methodSignature.getParameterNames()) {
if (parameterName.equals(paramName)) {
break;
}
index++;
}
Object paramObj = joinPoint.getArgs()[index];
if (ObjectUtil.isNull(paramObj)) {
throw new AopCommonException("找不到校验目标");
}
// 解析注解的内容
AccountParamType type = operateControl.paramType();
BaseResult baseResult;
// 分类校验
Long userIdLong = tokenManager.getUserIdFromToken();
String username = tokenManager.getUserNameFromToken();
String userId = userIdLong.toString();
localLog.info("操作人Id: {}, 用户名: {}", userId, username);
if (AccountParamType.TABLE_PRIMARY.equals(type)) {
// 是以表的主键为参数的
baseResult = handleTablePrimary(userId, paramObj.toString(), operateControl);
} else if (AccountParamType.ACCOUNT_PRIMARY.equals(type)) {
// 是以公众号id为参数的
baseResult = handleAccountPrimary(userId, paramObj.toString(), operateControl);
} else if (AccountParamType.REQUEST_BODY.equals(type)) {
// 是以包含公众号字段的实体为参数的
baseResult = handleRequestBody(userId, paramObj, operateControl);
} else {
baseResult = new BaseResult().error("权限校验异常-未知类型");
}
if (!baseResult.getSuccess()) {
throw new AopCommonException(baseResult.getMessage());
}
}
/**
* 校验以表主键id为参数的请求权限
* @param userId 用户名
* @param primaryId 表主键id
* @param annotation 注解
* @return 校验结果
*/
private BaseResult handleTablePrimary(String userId, String primaryId, AccountOperateControl annotation) {
if (!ObjectUtils.allNotNull(userId, primaryId, annotation.table(), annotation.column())) {
return new BaseResult().error("校验参数缺失");
}
// 通过参数获取这条数据对应的公众号的id
String accountParam = aopCommonMapper.getAccountFromTable(primaryId, annotation.table(), annotation.column());
localLog.info("TABLE_PRIMARY 类型 获取公众号ID: {}", accountParam);
// 调用方法处理username和公众号id的匹配关系
Boolean result = checkUserAccount(userId, accountParam, annotation.columnType());
return result ? new BaseResult().success() : new BaseResult().error("没有该公众号的操作权限");
}
/**
* 校验以公众号主键id为参数的请求
* @param userId 用户名
* @param accountParam 公众号主键id
* @return 检验结果
*/
private BaseResult handleAccountPrimary(String userId, String accountParam, AccountOperateControl annotation) {
localLog.info("ACCOUNT_PRIMARY 类型 获取公众号ID: {}", accountParam);
Boolean result = checkUserAccount(userId, accountParam,annotation.columnType());
return result ? new BaseResult().success() : new BaseResult().error("没有该公众号的操作权限");
}
/**
* 校验以实体类为参数的请求
* @param userId 用户名
* @param requestBody 实体类
* @param annotation 注解
* @return 校验结果
*/
private BaseResult handleRequestBody(String userId, Object requestBody, AccountOperateControl annotation) throws Exception {
// 获取实体中的公众号id字段值
Class<?> clazz = requestBody.getClass();
Field field = clazz.getDeclaredField(annotation.property());
field.setAccessible(true);
Object obj = field.get(requestBody);
if (ObjectUtil.isNull(obj)) {
return new BaseResult().error("无法获取公众号校验字段");
}
String accountParam = obj.toString();
localLog.info("REQUEST_BODY 类型 获取公众号ID: {}", accountParam);
Boolean result = checkUserAccount(userId, accountParam, annotation.columnType());
return result ? new BaseResult().success() : new BaseResult().error("没有该公众号的操作权限");
}
/**
* 最终校验
* @param userId 用户id
* @param accountParam 公众号相关参数(authId / appId)
* @param columnType 字段类型(authId / appId)
* @return 校验结果(true-通过;false-未通过)
*/
private Boolean checkUserAccount(String userId, String accountParam, AccountTableColumnType columnType) {
// 获取用户角色等级
Integer userRoleLevel = aopCommonMapper.getUserRoleLevel(userId);
if (userRoleLevel >= CustomerCommonConstant.PERMISSION_OPENING_ROLE_LEVEL) {
// 负责人及以上级别不加控制
return Boolean.TRUE;
}
QueryWrapper<AccountSetup> queryWrapper = new QueryWrapper<AccountSetup>().eq(AccountSetup.COL_USER_ID, userId);
if (AccountTableColumnType.AUTH_ID.equals(columnType)) {
queryWrapper = queryWrapper.eq(AccountSetup.COL_AUTH_ID, accountParam);
} else if (AccountTableColumnType.APP_ID.equals(columnType)) {
queryWrapper = queryWrapper.eq(AccountSetup.COL_APP_ID, accountParam);
}
int count = accountSetupService.count(queryWrapper);
return count != 0;
}
}
package com.yaoyaozw.customer.aop;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yaoyaozw.customer.annotations.OperateLog;
import com.yaoyaozw.customer.components.TokenManager;
import com.yaoyaozw.customer.entity.SystemOperateLog;
import com.yaoyaozw.customer.service.SystemOperateLogService;
import org.apache.commons.lang3.StringUtils;
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;
import java.util.Date;
/**
* @author darker
* @date 2022/12/9 11:41
*/
@Aspect
@Component
public class OperateLogAop {
private final static Integer LOG_PARAM_LENGTH_LIMIT = 200;
@Autowired
private TokenManager tokenManager;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SystemOperateLogService operateLogService;
@Pointcut("@annotation(com.yaoyaozw.customer.annotations.OperateLog)")
public void operateLogCut() {
//操作日志切点
}
@Around("operateLogCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
// 获取这个注解对应的方法
Class<?> targetClazz = point.getTarget().getClass();
MethodSignature signature = (MethodSignature)point.getSignature();
// 根据方法名和方法参数获取该方法的实体对象
Method targetMethod = targetClazz.getDeclaredMethod(signature.getName(), signature.getParameterTypes());
String param = objectMapper.writeValueAsString(point.getArgs());
if (StringUtils.isNotBlank(param) && param.length() > LOG_PARAM_LENGTH_LIMIT) {
param = param.substring(0, LOG_PARAM_LENGTH_LIMIT);
}
// 构造保存实体
SystemOperateLog operateLogEntity = SystemOperateLog.builder()
.methodPath(targetClazz.getName() + "." + targetMethod.getName())
.operateTime(new Date())
.requestParam(param)
.operateUser(tokenManager.getUserIdFromToken())
.build();
// 获取这个方法上的对应注解的实体
OperateLog operateLog = targetMethod.getAnnotation(OperateLog.class);
if (ObjectUtil.isNotNull(operateLog)) {
operateLogEntity.setOperateModule(operateLog.module());
operateLogEntity.setOperateDesc(operateLog.desc());
}
long start = System.currentTimeMillis();
try {
return point.proceed();
} catch (Throwable throwable) {
operateLogEntity.setOperateResult(Boolean.FALSE);
throw throwable;
}finally {
// 统计处理时间, 保存操作记录
long end = System.currentTimeMillis();
operateLogEntity.setTimeCost(end - start);
operateLogService.save(operateLogEntity);
}
}
}
......@@ -12,6 +12,8 @@ import java.util.List;
*/
public class CustomerCommonConstant {
public final static Integer PERMISSION_OPENING_ROLE_LEVEL = 3;
public final static String CROWD_HUMAN_NUN_REDIS_KEY = "crowdHumanNum";
public final static String STORE_NAME_YANG_GUANG = "YANG_GUANG";
......
package com.yaoyaozw.customer.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Author didi
* @Create 2022/4/28 15:15
*/
@ApiModel(value="account_setup")
@Data
@TableName(value = "account_setup")
public class AccountSetup {
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value="")
private Long id;
@TableField(value = "auth_id")
@ApiModelProperty(value="")
private Long authId;
@TableField(value = "app_id")
@ApiModelProperty(value="")
private String appId;
@TableField(value = "user_id")
@ApiModelProperty(value="")
private String userId;
public static final String COL_ID = "id";
public static final String COL_AUTH_ID = "auth_id";
public static final String COL_APP_ID = "app_id";
public static final String COL_USER_ID = "user_id";
}
\ No newline at end of file
package com.yaoyaozw.customer.entity;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.example.material.constant.MaterialCommonConstant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
/**
* 材料操作日志
*
* @author darker
* @date 2022/12/09
*/
@Data
@TableName("system_operate_log")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SystemOperateLog implements Serializable {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 方法路径
*/
@TableField("method_path")
private String methodPath;
/**
* 请求参数
*/
@TableField("request_param")
private String requestParam;
/**
* 执行模块
*/
@TableField("operate_module")
private String operateModule;
/**
* 执行描述
*/
@TableField("operate_desc")
private String operateDesc;
/**
* 执行结果
*/
@TableField("operate_result")
private Boolean operateResult;
/**
* 执行时间
*/
@TableField("operate_time")
private Date operateTime;
/**
* 执行人
*/
@TableField("operate_user")
private Long operateUser;
/**
* 方法耗时
*/
@TableField("time_cost")
private BigDecimal timeCost;
public Boolean getOperateResult() {
return ObjectUtil.isNull(this.operateResult) ? Boolean.TRUE : this.operateResult;
}
public void setTimeCost(Long time) {
if (ObjectUtil.isNotNull(time)) {
this.timeCost = new BigDecimal(time).divide(MaterialCommonConstant.THOUSAND_BIG_DECIMAL, 3, RoundingMode.HALF_UP);
}
}
private static final long serialVersionUID = 1L;
}
package com.yaoyaozw.customer.enums;
/**
* @author darker
* @date 2022/12/13 11:59
*/
public enum AccountParamType {
/**
* 业务操作时,公众号参数的存在形式
*/
// 以数据表主键id作为参数的形式
TABLE_PRIMARY,
// 以公众号id作为参数的形式
ACCOUNT_PRIMARY,
// 参数是实体参数
REQUEST_BODY;
}
package com.yaoyaozw.customer.enums;
/**
* @author darker
* @date 2022/12/13 11:59
*/
public enum AccountTableColumnType {
/**
* 业务操作时,公众号参数的存在形式
*/
// 表中的公众号字段是appId
APP_ID,
// 表中的公众号字段是主键id
AUTH_ID;
}
package com.yaoyaozw.customer.exception;
/**
* @author 10626
*/
public class AopCommonException extends RuntimeException {
private static final long serialVersionUID = -5355745971968165717L;
public AopCommonException() {
}
public AopCommonException(String message) {
super(message);
}
}
package com.yaoyaozw.customer.exception;
/**
* @author 10626
*/
public class BaseException extends RuntimeException {
private static final long serialVersionUID = -5355745971968165717L;
public BaseException() {
}
public BaseException(String message) {
super(message);
}
}
package com.yaoyaozw.customer.exception;
import cn.hutool.core.text.StrBuilder;
import com.yaoyaozw.customer.common.BaseResult;
import com.yaoyaozw.customer.utils.LogUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* @author 10626
*/
@Slf4j
@RestControllerAdvice(annotations = RestController.class)
public class ExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public BaseResult handleValidException(MethodArgumentNotValidException e) {
log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult = e.getBindingResult();
// 将校验信息直接拼接进行返回
StrBuilder strBuilder = new StrBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
strBuilder.append(fieldError.getDefaultMessage()).append("\n");
}
return new BaseResult().error(strBuilder.toString());
}
@ExceptionHandler(value = AopCommonException.class)
public BaseResult aopCommonException(AopCommonException e) {
String msg = LogUtils.getStackTraceInfo(e);
log.error("AOP异常-> {}", msg);
return new BaseResult().error(e.getMessage());
}
@ExceptionHandler(value = BaseException.class)
public BaseResult baseException(BaseException e) {
String msg = LogUtils.getStackTraceInfo(e);
log.error("系统出现异常{}", msg);
return new BaseResult().error(e.getMessage());
}
@ExceptionHandler(value = Exception.class)
public BaseResult handleException(Exception e) {
log.error("错误:{}", LogUtils.getStackTraceInfo(e));
e.printStackTrace();
return new BaseResult().error(e.getMessage());
}
}
package com.yaoyaozw.customer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yaoyaozw.customer.entity.AccountSetup;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @Author didi
* @Create 2022/4/28 15:15
*/
@Mapper
public interface AccountSetupMapper extends BaseMapper<AccountSetup> {
}
\ No newline at end of file
package com.yaoyaozw.customer.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* @author darker
* @date 2022/12/13 15:51
*/
@Repository
public interface MaterialAopCommonMapper {
/**
* 从表获取帐户
*
* @param primaryId 主键id
* @param tableName 表名
* @param columnName 列名
* @return {@link String}
*/
String getAccountFromTable(@Param("primaryId") String primaryId, @Param("tableName") String tableName, @Param("columnName") String columnName);
/**
* 得到用户角色级别
*
* @param userId 用户id
* @return {@link Integer}
*/
Integer getUserRoleLevel(@Param("userId") String userId);
}
package com.yaoyaozw.customer.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yaoyaozw.customer.entity.SystemOperateLog;
import org.springframework.stereotype.Repository;
/**
* @author darker
* @date 2022/12/9 12:11
*/
@Repository
public interface SystemOperateLogMapper extends BaseMapper<SystemOperateLog> {
}
package com.yaoyaozw.customer.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yaoyaozw.customer.entity.AccountSetup;
import java.util.List;
/**
* @Author didi
* @Create 2022/4/28 15:15
*/
public interface AccountSetupService extends IService<AccountSetup> {
}
package com.yaoyaozw.customer.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yaoyaozw.customer.entity.SystemOperateLog;
/**
* @author darker
* @date 2022/12/9 12:11
*/
public interface SystemOperateLogService extends IService<SystemOperateLog> {
}
package com.yaoyaozw.customer.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yaoyaozw.customer.entity.AccountSetup;
import com.yaoyaozw.customer.mapper.AccountSetupMapper;
import com.yaoyaozw.customer.service.AccountSetupService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author didi
* @Create 2022/4/28 15:15
*/
@Service
public class AccountSetupServiceImpl extends ServiceImpl<AccountSetupMapper, AccountSetup> implements AccountSetupService {
}
package com.yaoyaozw.customer.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yaoyaozw.customer.entity.SystemOperateLog;
import com.yaoyaozw.customer.mapper.SystemOperateLogMapper;
import com.yaoyaozw.customer.service.SystemOperateLogService;
import org.springframework.stereotype.Service;
/**
* @author darker
* @date 2022/12/9 12:11
*/
@Service
public class SystemOperateLogServiceImpl extends ServiceImpl<SystemOperateLogMapper, SystemOperateLog> implements SystemOperateLogService {
}
package com.yaoyaozw.customer.utils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @author 10626
*/
public class LogUtils {
/**
* 获取e.printStackTrace() 的具体信息,赋值给String 变量,并返回
*
* @param e
* Exception
* @return e.printStackTrace() 中 的信息
*/
public static String getStackTraceInfo(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);//将出错的栈信息输出到printWriter中
pw.flush();
sw.flush();
return sw.toString();
} catch (Exception ex) {
return "printStackTrace()转换错误";
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (pw != null) {
pw.close();
}
}
}
}
......@@ -12,7 +12,7 @@ spring:
nacos:
discovery:
server-addr: 47.103.117.175:8848
namespace: 062507b6-b1d7-4aac-8ed8-0da1cd9451e1
namespace: 697c082b-664c-4b37-a3a9-4328df43a8bc
config:
server-addr: 47.103.117.175:8848
namespace: 062507b6-b1d7-4aac-8ed8-0da1cd9451e1
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yaoyaozw.customer.mapper.AccountSetupMapper">
<resultMap id="BaseResultMap" type="com.yaoyaozw.customer.entity.AccountSetup">
<!--@mbg.generated-->
<!--@Table account_setup-->
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="auth_id" jdbcType="BIGINT" property="authId"/>
<result column="app_id" jdbcType="VARCHAR" property="appId"/>
<result column="user_id" jdbcType="CHAR" property="userId"/>
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, auth_id, app_id, user_id, appid
</sql>
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论