aspect切面,记录日志,权限,防止重复提交(Redis)

2 篇文章 0 订阅
2 篇文章 0 订阅

一、切面记录日志

1.首先定义自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
    int type() default 0;
}

2.创建切面LogAspect.Java

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Resource
    private SysLogService logService;

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.xxxx.xxxxx.util.Log)")
    public void logPointcut() {
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        SysUser currentUser = UserUtils.getCurrentUser();
        currentTime.set(System.currentTimeMillis());
        result = joinPoint.proceed();
        SysLog log = new SysLog("INFO", System.currentTimeMillis() - currentTime.get());
        currentTime.remove();
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        if(currentUser != null) {
            logService.saveLog(currentUser.getName(), IpAddressUtil.getIpAddr(request), joinPoint,
                    log, currentUser.getId());
        } else {
            Object userInfo = ((HashMap) ((R) result).getData()).get("userInfo");
            logService.saveLog(((UserVO) userInfo).getName(), IpAddressUtil.getIpAddr(request), joinPoint,
                    log, ((UserVO) userInfo).getId());
        }
        return result;
    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        SysUser currentUser = UserUtils.getCurrentUser();
        SysLog log = new SysLog("ERROR", System.currentTimeMillis() - currentTime.get());
        currentTime.remove();
        log.setExceptionDetail(ThrowableUtil.getStackTrace(e));
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        if(currentUser != null){
            logService.saveLog(currentUser.getName(), IpAddressUtil.getIpAddr(request),
                    (ProceedingJoinPoint) joinPoint, log, currentUser.getId());
        } else {
            logService.saveLog(null, IpAddressUtil.getIpAddr(request), (ProceedingJoinPoint) joinPoint, log, null);
        }
    }
}

 3.保存log的service方法

public boolean saveLog(String username, String ip, ProceedingJoinPoint joinPoint, SysLog sysLog, Long uid) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log aopLog = method.getAnnotation(Log.class);

        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";

        StringBuilder params = new StringBuilder("{");

        // 描述
        if (sysLog != null) {
            sysLog.setDescription(aopLog.value());
        }

        if (uid != null) {
            sysLog.setUid(uid);
        }
        assert sysLog != null;
        sysLog.setRequestIp(ip);

        //参数值
        Object[] argValues = joinPoint.getArgs();

        String loginPath = "login";
        if (loginPath.equals(signature.getName())) {
            try {
                //参数名称
                String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
                if (argValues != null) {
                    params.append(" ").append(argNames[1]).append(": ").append(argValues[1]);
                }
                assert argValues != null;
                String phone = new JSONObject(argValues[1]).get("phone").toString();
                username = userService.getByPhone(phone).getName();
            } catch (Exception e) {
                log.error("插入登录日志错误:{}", e);
            }
        } else {
            //参数名称
            String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            if (argValues != null) {
                for (int i = 0; i < argValues.length; i++) {
                    //编辑文章内容太长,导致数据库查询很卡,所以日志内容直接截取
                    String argValueString = argValues[i].toString();
                    if (argValueString.length() > 10000) {
                        argValueString = argValueString.substring(0, 10000);
                    }
                    params.append(" ").append(argNames[i]).append(": ").append(argValueString);
                }
            }
        }
        sysLog.setIpSource(StringUtils.getCityInfo(sysLog.getRequestIp()));
        sysLog.setMethod(methodName);
        sysLog.setUsername(username);
        sysLog.setParams(params.toString() + " }");
        return super.save(sysLog);
    }

二、切面控制权限

1. 创建自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthAnnotation {

    String value();
}

 2.切面方法

@Aspect
@Component
@Slf4j
public class AuthorizeAspect {

    @Resource
    private SysRolePermissionService rolePermissionService;

    @Pointcut("@annotation(com.xxx.xxxxx.util.AuthAnnotation)")
    public void AuthLoginVerify() {

    }

    @Before("AuthLoginVerify()")
    public void doAdminAuthVerify(JoinPoint joinPoint) {
        // 判断是否进行权限验证
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //从切面中获取当前方法
        Method method = signature.getMethod();
        //得到了方,提取出他的注解
        AuthAnnotation action = method.getAnnotation(AuthAnnotation.class);
        // 进行权限验证
        SysUser loginUser = UserUtils.getCurrentUser();
        authRuleVerify(action.value(), loginUser.getId());
    }

    /**
     * 权限验证
     *
     * @param authPermission
     */
    private void authRuleVerify(String authPermission, Long userId) {

        if (authPermission != null && authPermission.length() > 0) {
            List<String> permission = rolePermissionService.getPermissionIds(userId);
            PredicateOptional.of(permission.size()).ifTrueThrow(size -> size == 0, "当前用户还未分配角色,请联系管理员分配!");
            if (!permission.contains(authPermission)) {
                throw new BusinessException("暂无操作权限!!");
            }
        }
    }
}

三、防止接口在指定时间内重复提交

1.创建自定义注解

@Aspect
@Slf4j
@Component
@RequiredArgsConstructor
public class RepeatCommitAspectAmend {

    private final RedisLock redisLock;

    @Pointcut("@annotation(forbidRepeatCommit)")
    public void pointCut(ForbidRepeatCommit forbidRepeatCommit) {
    }

    @Around("pointCut(forbidRepeatCommit)")
    public Object around(ProceedingJoinPoint pjp, ForbidRepeatCommit forbidRepeatCommit) throws Throwable {
        int lockSeconds = forbidRepeatCommit.lockTime();

        HttpServletRequest request = getRequest();
        Assert.notNull(request, "request can not null");
        String bearerToken = request.getHeader("authorization");
        String[] tokens = bearerToken.split(" ");
        String token = tokens[1];
        String path = request.getServletPath();
        String key = getKey(token, path);
        String clientId = getClientId();

        Signature signature = pjp.getSignature();
        // 参数名数组
        String[] parameterNames = ((MethodSignature) signature).getParameterNames();
        // 构造参数组集合
        List<Object> argList = new ArrayList<>();
        for (Object arg : pjp.getArgs()) {
            // request/response无法使用toJSON
            if (arg instanceof HttpServletRequest) {
                argList.add("request");
            } else if (arg instanceof HttpServletResponse) {
                argList.add("response");
            } else {
                argList.add(JSON.toJSON(arg));
            }
        }

        Object previousId = ((JSONObject) argList.get(0)).get("previousId");
        boolean isSuccess = redisLock.tryLock(previousId+"", clientId, lockSeconds);
        log.info("tryLock key = [{}], clientId = [{}]", key, clientId);

        if (isSuccess) {
            log.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
            // 获取锁成功
            Object result;

            try {
                // 执行进程
                result = pjp.proceed();
            } finally {
                // 解锁
                //redisLock.releaseLock(key, clientId);
                log.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
            }

            return result;

        } else {
            // 获取锁失败,认为是重复提交的请求
            log.info("tryLock fail, key = [{}]", key);
            return R.fail(ErrorCode.DIAGNOSIS_REPEAT_OPERATION, ErrorCode.DIAGNOSIS_REPEAT_OPERATION.getMsg());
        }
    }

    private String getKey(String token, String path) {
        return token + path;
    }

    private String getClientId() {
        return UUID.randomUUID().toString();
    }

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return ra.getRequest();
    }

3.RedisLock.Java

@Service
@RequiredArgsConstructor
public class RedisLock {

    private final RedisUtils redisUtils;

    public boolean tryLock(String lockKey, String clientId, long seconds) {
        if(redisUtils.hasKey(lockKey)){
            return false;
        }
        return redisUtils.set(lockKey, clientId, seconds);
    }

    public void releaseLock(String lockKey, String clientId) {
        redisUtils.del(lockKey);
    }
}

4.RedisUtils.Java

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值