目的,在每次请求的时候,对每次传的参数,进行解析,验签,并返回解码后的参数, 以json传递;
例子背景:
IOT平台提供对外可访问的接口, 需要对所有参数的传递做到 不泄露、认证的目的;所以需要在每次请求的时候:
1、对需要传递的参数进行加密,
byte[] encrypt = encrypt(content, "6AEB57F8DA93860A19514592A154BEF8");
 
 
String hexStr = parseByte2HexStr(encrypt);
System.out.println("加密后的2进制密文:" + hexStr);
2、通过对时间戳、随机数、请求类型、路径和加密后的参数进行加签
String method = "POST";
String timestamp = String.valueOf(System.currentTimeMillis());
System.out.println("timestamp:"+timestamp);
String salt = String.valueOf((int)((Math.random()*9+1)*100000));
System.out.println("salt:"+salt);
String url  ="/iot/open/device/v1/sync";
String sign = generateSign(salt,timestamp,"407af810068111ea95f4852d6a259567",method,url,hexStr, "b645d880068111ea8f09cf8592eb9fbc");
System.out.println("sign:"+sign);
 
/**
* 签名算法
*
* @param salt 随机数
* @param timestamp 时间戳
* @param appKey 应用Key
* @param httpRequestMethod 添加POST
* @param canonicalURI 接口地址
* @param canonicalParameterString 加签内容
*/
public static String generateSign(String salt, String timestamp, String appKey,
    String httpRequestMethod, String canonicalURI, String canonicalParameterString,
    String appSecret) {
    String sign = null;
    try {
        String canonicalHeaders = salt + timestamp + appKey;
        String stringToSign = httpRequestMethod + "\n"
            + canonicalURI + "\n"
            + canonicalParameterString + "\n"
            + canonicalHeaders;
        Mac mac = null;
        mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec sk = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8),
            "HmacSHA256");
        mac.init(sk);
        sign = Hex
            .encodeHexString(mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)));
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        log.error(
            "生成签名失败,appKey:{},salt:{},timestamp:{},httpRequestMethod:{},canonicalURI:{},canonicalParameterString:{}",
            appKey, salt, timestamp, httpRequestMethod, canonicalURI, canonicalParameterString);
        e.printStackTrace();
    }
    return sign;
}
3、最后发送请求
 
此时,参数是加密的,请求头中包含着约定的 签名、时间戳、随机数、约定的key; 已经对传递的参数做了加密,页身份的加签
 
后面开始 切面编程:
 
4、创建注解:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SignValidate {
    /**
     * 方法上用于接收接收业务参数字段名称
     *
     * @return 参数名称
     */
    String paramName() default "signParam";
    /**
     * 参数中加签字段的名称
     *
     * @return 加签字段的名称
     */
    String signName() default "parameters";
}
使用的时候
只需要在方法上加上:
@SignValidate
@PostMapping("/v1/sync")
public CommonResponse syncOne(HttpServletRequest request,
    @RequestBody SignParamDTO signParam) {
    log.info("设备同步请求ip为{},参数为{}", WebUtils.getIp(request), signParam.getParameters());
    return CommonResponse.success(deviceSyncService.syncOne(signParam.getParameters()));
}
 
5、开始切面类
HttpServletRequest的功能可以分为以下几种:
封装了请求头数据;
封装了请求正文数据,如果是GET请求,那么就没有正文;
request是一个域对象,可以把它当成Map来添加获取数据;
做请求的转发
package cn.video110.iot.open.aspect;
 
 
import cn.video110.iot.base.errorcode.SignErrorCode;
import cn.video110.iot.open.utils.SignUtil;
import cn.video110.starter.mvc.common.CommonResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
 
 
/**
* 参数签名验证
* @author 
* @date 2020/07/23
*/
@Slf4j
@Aspect //参数
@Component //组件
public class SignValidateAspect {
 
 
    private static String APP_KEY = "407af810068111ea95f4852d6a259567";
    private static String APP_SECRET = "b645d880068111ea8f09cf8592eb9fbc";
    private static String APP_PASSWORD = "6AEB57F8DA93860A19514592A154BEF8";
    /**
     * header 中验签参数
     */
    private final String[] SIGN_HEADER = new String[]{
        "x-clss-iot-authorization",
        "x-clss-iot-timestamp",
        "x-clss-iot-salt",
        "x-clss-iot-appkey"
    };
    private final String headerAuthorization = "x-clss-iot-authorization";
    private final String headerTimestamp = "x-clss-iot-timestamp";
    private final String headerSalt = "x-clss-iot-salt";
    private final String headerAppKey = "x-clss-iot-appkey";
 
 
    /**
     * 签名验证  表示所有的带有SignValidate的注解
     */
    @Pointcut("@annotation(cn.video110.iot.open.aspect.SignValidate)")
    public void signValidate() {
    }
 
 
    @Around("signValidate()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object response = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes)
            RequestContextHolder.getRequestAttributes();
            // 获取当前的Request的实体
        HttpServletRequest request = attributes.getRequest();
        Map<String, String> signHeader = getSignHeader(request);
        //校验header
        CommonResponse checkResult = checkHeader(signHeader);
        if (!checkResult.isStatus()) {
            return checkResult;
        }
        checkResult = verifySignAndDecrypt(request, signHeader, joinPoint);
        if (!checkResult.isStatus()) {
            return checkResult;
        }
        response = joinPoint.proceed((Object[]) checkResult.getObj());
        return response;
    }
 
 
    /**
     * 获取header签名用的字段
     */
    private Map<String, String> getSignHeader(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> headerMap = new HashMap();
        List<String> signList = Arrays.asList(SIGN_HEADER);
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            if (signList.contains(headerName)) {
                String headerValue = request.getHeader(headerName);
                headerMap.put(headerName, headerValue);
            }
        }
        return headerMap;
    }
 
 
    /**
     * 校验header内参数
     *
     * @param signHeader header签名参数
     * @return CommonResponse 校验结果
     */
    private CommonResponse checkHeader(Map<String, String> signHeader) {
        if (!signHeader.containsKey(headerAuthorization) || StringUtils
            .isBlank(signHeader.get(headerAuthorization))) {
            return CommonResponse.error(SignErrorCode.NON_AUTHORIZATION);
        }
        if (!signHeader.containsKey(headerTimestamp) || StringUtils
            .isBlank(signHeader.get(headerTimestamp))) {
            return CommonResponse.error(SignErrorCode.NON_TIMESTAMP);
        }
        if (!signHeader.containsKey(headerSalt) || StringUtils
            .isBlank(signHeader.get(headerSalt))) {
            return CommonResponse.error(SignErrorCode.NON_SALT);
        }
        if (!signHeader.containsKey(headerAppKey) || StringUtils
            .isBlank(signHeader.get(headerAppKey))) {
            return CommonResponse.error(SignErrorCode.NON_APPKEY);
        }
        return CommonResponse.success();
    }
 
 
    private CommonResponse<Object[]> verifySignAndDecrypt(HttpServletRequest request,
        Map<String, String> signHeader,
        ProceedingJoinPoint joinPoint) {
        //获取参数
        Object[] args = joinPoint.getArgs();
        CommonResponse response = null;
            // 获取连接点的方法签名对象;
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] params = methodSignature.getParameterNames();
        if (params.length == 0) {
            log.info("当前切入点方法{}没有需要验签的参数。", methodSignature.getName());
            return CommonResponse.success(args);
        }
        // 获得注解中的信息
        Method method = methodSignature.getMethod();
        SignValidate signValidate = method.getAnnotation(SignValidate.class);
        Integer index = ArrayUtils.indexOf(params, signValidate.paramName());
        if (args[index] != null) {
            String sign = getSign(args[index], signValidate.signName());
            if (StringUtils.isBlank(sign)) {
                return CommonResponse.error(SignErrorCode.NON_PARAM);
            }
            response = verifySign(request, signHeader, sign);
            if (!response.isStatus()) {
                return response;
            }
            args[index] = decryptParam(args[index], signValidate.signName(), APP_PASSWORD);
 
 
        }
        return CommonResponse.success(args);
    }
 
 
 
 
    /**
     * 验签
     *
     * @param request 请求
     * @param signHeader 签名header
     * @param parameters 参数
     * @return 验签结果
     */
    private CommonResponse verifySign(HttpServletRequest request, Map<String, String> signHeader,
        String parameters) {
        //根据appKey查询
        String appSecret = APP_SECRET;
        //验签
        String method = request.getMethod();
        String canonicalURI = request.getServletPath() + request.getPathInfo();
        String salt = signHeader.get(headerSalt);
        String timestamp = signHeader.get(headerTimestamp);
        String appKey = signHeader.get(headerAppKey);
        String sign = SignUtil
            .generateSign(salt, timestamp, appKey, method, canonicalURI, parameters, appSecret);
        if (!Objects.equals(sign, signHeader.get(headerAuthorization))) {
            return CommonResponse.error(SignErrorCode.VERIFY_FAIL);
        }
        return CommonResponse.success();
    }
 
 
    private String getSign(Object signObject, String signName) {
        Class<?> resultClass = signObject.getClass();
        Field[] fieldInfo = resultClass.getDeclaredFields();
        for (Field field : fieldInfo) {
            if (signName.equals(field.getName())) {
                field.setAccessible(true);
                Object fieldValue = null;
                try {
                    fieldValue = field.get(signObject);
                    if (fieldValue == null) {
                        return null;
                    }
                    return fieldValue.toString();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
        return null;
    }
 
 
 
 
    /**
     * 解密参数
     *
     * @param args 入参实体
     * @param signName 签名字段名称
     * @param appPassword 解密密码
     * @return 解密后的参数
     */
    private Object decryptParam(Object args, String signName, String appPassword) {
        Class<?> resultClass = args.getClass();
        Field[] fieldInfo = resultClass.getDeclaredFields();
        for (Field field : fieldInfo) {
            if (signName.equals(field.getName())) {
                field.setAccessible(true);
                Object fieldValue = null;
                try {
                    fieldValue = field.get(args);
                    String decryptValue = SignUtil.decrypt(fieldValue.toString(), appPassword);
                    field.set(args, decryptValue);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
        return args;
    }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

SpringBoot中使用切面的每次传的参数,进行解析,验签,并返回解码后的参数的更多相关文章

  1. 在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库

    文章目录 1.导入相关的依赖 2.创建要保存的数据信息实体类 3 .编写对应的sql语句 4.使用spring 的 aop 技术切到自定义注解上,所以先创建一个自定义注解类 5. 创建aop切面实现类 ...

  2. 如何让springboot中的某些html文件不经过thymeleaf模板解析?

    这个thymeleaf有时有用,有时用不着. 但默认设置,所有的html都会经过它解析. 我的作法,是新建public,在resource里,所有css,js所放里面.(当然,static下也是OK的 ...

  3. springBoot中使用使用junit测试文件上传,以及文件下载接口编写

    本篇文章将介绍如何使junit在springBoot中测试文件的上传,首先先阅读如何在springBoot中进行接口测试. 文件上传操作测试代码 import org.junit.Before; im ...

  4. Springboot中Aspect实现切面(以记录日志为例)

    前言今天我们来说说spring中的切面Aspect,这是Spring的一大优势.面向切面编程往往让我们的开发更加低耦合,也大大减少了代码量,同时呢让我们更专注于业务模块的开发,把那些与业务无关的东西提 ...

  5. SpringBoot中使用Maven插件,上传docker镜像

    开启docker远程端口 我上一篇里面写了,这里暴露的路径: 18.16.202.95:2375 简单构建 配置pom.xml文件 在properties中增加一行指定远程主机的位置 <prop ...

  6. 由浅入深学习springboot中使用redis

    很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 ...

  7. springboot中使用自定义两级缓存

    工作中用到了springboot的缓存,使用起来挺方便的,直接引入redis或者ehcache这些缓存依赖包和相关缓存的starter依赖包,然后在启动类中加入@EnableCaching注解,然后在 ...

  8. (一)由浅入深学习springboot中使用redis

    很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 ...

  9. SpringBoot图文教程5—SpringBoot 中使用Aop

    有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 文章结尾配套自测面试题,学完技术自我测试更扎实 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例 ...

随机推荐

  1. Dataway与SpringBoot集成干掉后台开发

    Dataway与SpringBoot集成干掉后台开发 Dataway让SpringBoot不在需要Controller.Service.DAO.Mapper了. 第一步:引入相关依赖 <depe ...

  2. java中Math的常用方法整理

    public class Demo{ public static void main(String args[]){ /** *Math.sqrt()//计算平方根 *Math.cbrt()//计算立 ...

  3. linux驱动之模块化编程小总结

    本文包含了linux驱动模块化编程的基本,包括创建多线程,延时,以及makefile 以一个实例来说明 #include<linux/init.h> #include<linux/m ...

  4. hook框架-frida使用-环境配置

    一.python安装模块 pip3 install frida pip3 install frida-tools 二.下载frida-server #下载链接 https://github.com/f ...

  5. 【接口自动化】Python+Requests接口自动化测试框架搭建【二】

    接续前文,在上篇博客中我们编写了demo.py代码,里面代码过多冗余,更新代码: #!/usr/bin/env python # coding=utf-8 import requests class ...

  6. ASP.Net中的async+await异步编程

    在.NET Framework4.5框架.C#5.0语法中,通过async和await两个关键字,引入了一种新的基于任务的异步编程模型(TAP).在这种方式下,可以通过类似同步方式编写异步代码,极大简 ...

  7. http请求工作流程

    一.HTTP工作原理 HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端.HTTP协议采用了请求/响应模型.客户端向服务器发送一个请求报文,请求报文包 ...

  8. springSecurity初识-练气初期

    1.写在前面 Spring Security是一个框架,提供针对常见攻击的身份验证,授权和保护.通过对命令式和反应式应用程序的一流支持,它是保护基于Spring的应用程序的事实标准. Spring S ...

  9. AltiumDesigner20画图不求人10 | 提高AD20启动速度的方法六取消加入产品改善计划 | 视频教程 | 你问我答

    教程内容:AltiumDesigner20画图不求人系列,是电子芯原创的AltiumDesigner绘图技巧视频教程,每一个技巧只需要不到3分钟的时间就可以完成学习.前期经过AD19的画图不求人,帮助 ...

  10. 基于函数的I/O操作(头文件stdio.h)

    基于函数库的I/O是C语言标准库的功能,基于系统级I/O函数实现. 系统级I/O函数对文件的标识是文件描述符,C语言标准库中对文件的标识是指向FILE结构的指针.在头文件cstdio或stdio.h中 ...