当前位置: 首页 > news >正文

AOP切面实现增删改防止重放攻击

前言

认证重放攻击(高风险)

风险级别: 高风险

风险描述: 攻击者发送一个目标主机已经接收的数据包,特别是在认证过程中,用于认证用户身份时;

风险分析: 攻击者可以使用重放攻击方式伪装成用户,冒充用户身份进行一系列操作;


实现思路

        由前端在所有crud等接口在header中全局加上签名【sign】,签名由MD5与AES加密而成(可根据实际生产环境自由决定),然后后端在切片中过滤增删改的接口进行签名认证,同一个签名只在redis中留存一段时间,防止重放攻击;其他不需要防重放的接口(如查询)则跳过验证签名。


自定义注解

这里沿用自定义的接口操作写入日志注解【OperLog】

import java.lang.annotation.*;

@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperLog
{
    String operModul() default ""; // 操作模块
    String operType() default "";  // 操作类型
    String operDesc() default "";  // 操作说明
    // 事件级别
    String operLevel() default "低";
}

切片实现

首先明确AOP中几个注解的执行顺序

@Around注解方法的前半部分业务逻辑
->@Before注解方法的业务逻辑
->目标方法的业务逻辑
->@Around注解方法的后半部分业务逻辑(@Around注解方法内的业务逻辑若对ProceedingJoinPoint.proceed()方法没做捕获异常处理,直接向上抛出异常,则不会执行Around注解方法的后半部分业务逻辑;若做了异常捕获处理,则会执行)。
->@After(不管目标方法有无异常,都会执行@After注解方法的业务逻辑)
->@AfterReturning(若目标方法无异常,执行@AfterReturning注解方法的业务逻辑)
->@AfterThrowing(若目标方法有异常,执行@AfterThrowing注解方法的业务逻辑)

切面代码如下

@Aspect
@Component
public class OperLogAspect {

    @Autowired
    private StringRedisService redisService;


    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     * <功能详细描述>
     *
     * @see [类、类#方法、类#成员]
     */
    @Pointcut("@annotation(com.bw.dsm.config.aop.OperLog)")
    public void operLogPoinCut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     * <功能详细描述>
     *
     * @see [类、类#方法、类#成员]
     */
    @Pointcut("execution(* com.bw.dsm.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    @Around(value = "operLogPoinCut()")
    public Object authorityHandler(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取到请求对象
        HttpServletRequest request = attributes.getRequest();
        Boolean isValid = true;
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            OperLog opLog = method.getAnnotation(OperLog.class);
            if (opLog != null) {
                String operDesc = opLog.operDesc();
                // 从数据库中获取AES加密密钥
                String secKey = sysUserMapper.selectParamByName("sec_key", "sec_key");
                if ("新增".equals(operDesc) || "修改".equals(operDesc)
                        || "删除".equals(operDesc) || "新增或编辑".equals(operDesc)) {
                    String sign = request.getHeader("sign");
                    sign = decrypt(sign, secKey);
                    Object obj = redisService.hmGet("signKey", sign);
                    if (obj != null) {
                        isValid = false;
                    }
                    redisService.hmSetByTime("signKey", sign, sign, 5000L);
                }
            }
            if(isValid == false){
                return returnLimit(request);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return joinPoint.proceed();
    }

    public static String decrypt(String sSrc, String sKey) throws Exception {
       try {  
           // 判断Key是否正确  
           if (sKey == null) {  
               System.out.print("Key为空null");  
               return null;  
           }  
           // 判断Key是否为16位  
           if (sKey.length() != Constant.GLOBAL_INT_SIXTEEN) {
               System.out.print("Key长度不是16位");  
               return null;  
           }  
           byte[] raw = sKey.getBytes("utf-8");  
           SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");  
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");  
           IvParameterSpec iv = new IvParameterSpec(sKey.getBytes());  
           cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
           //先用base64解密
           byte[] encrypted1 = new Base64().decode(sSrc);
           try {  
               byte[] original = cipher.doFinal(encrypted1);  
               String originalString = new String(original,"utf-8");  
               return originalString;  
           } catch (Exception e) {  
               System.out.println(e.toString());  
               return null;  
           }  
       } catch (Exception ex) {  
           System.out.println(ex.toString());  
           return null;  
       }  
    }
  

    private String returnLimit(HttpServletRequest request) throws Throwable {

        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getResponse();
        JSONObject obj = new JSONObject();
        obj.put("errcode", "0209");
        obj.put("errmsg", "签名错误");
        response.setHeader("content-type", "text/html;charset=UTF-8");
        response.getWriter().print(obj);
        response.getWriter().flush();
        return null;
    }
}

注解使用

@PostMapping("/delLog")
@OperLog(operType = Constant.STRING_THREE,operModul = "日志删除" , operDesc = "删除", operLevel = "高")
public RestResult delLog(@RequestBody RequestParam param)
        throws Exception {
    return demandService.delLog(param.getShId());
}

相关文章:

  • oracle数据库 表中有数据,通过plsql 工具 连接 查询全表,却查不到数据
  • 第14章Linux实操篇-RPM与YUM
  • 小程序 input type=‘number‘ 不能输入小数点??
  • 高质量的子程序
  • 软件测试时Java面试题
  • 业务提前初始化执行
  • 区块链——Hyperledger Fabric2.2单点搭建网络
  • 从零开发一款图片编辑器Mitu-Dooring
  • 2022-08-30 第六小组 瞒春 学习笔记
  • 记录k8s-Calico网络插件报错问题
  • 北大肖臻老师《区块链技术与应用》系列课程学习笔记[25]以太坊-智能合约-5
  • 技术对接36
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • 【编程强训11】最近公共祖先+求最大连续bit数
  • 走进Redis之配置文件的修改使用
  • 【mysql】环境安装、服务启动、密码设置
  • C++11: atomic 头文件
  • ES2017异步函数现已正式可用
  • FineReport中如何实现自动滚屏效果
  • leetcode46 Permutation 排列组合
  • Redis在Web项目中的应用与实践
  • select2 取值 遍历 设置默认值
  • spring学习第二天
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • vue:响应原理
  • 复杂数据处理
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 一些关于Rust在2019年的思考
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 自制字幕遮挡器
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​VRRP 虚拟路由冗余协议(华为)
  • #vue3 实现前端下载excel文件模板功能
  • (13)Hive调优——动态分区导致的小文件问题
  • (9)STL算法之逆转旋转
  • (WSI分类)WSI分类文献小综述 2024
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)C语言家族扩展收藏 (转)C语言家族扩展
  • (转)EOS中账户、钱包和密钥的关系
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET Standard 的管理策略
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .Net 应用中使用dot trace进行性能诊断
  • //解决validator验证插件多个name相同只验证第一的问题
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [20190401]关于semtimedop函数调用.txt
  • [383] 赎金信 js
  • [Angular] 笔记 7:模块