目的,在每次请求的时候,对每次传的参数,进行解析,验签,并返回解码后的参数, 以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. 一句话木马变形(截止2020年8月16日通杀D盾、安全狗,微步,webshellKiller)

    首先一句话木马: <?php assert($_POST['a']); ?> D盾扫描,5级 分开写: <?php $a = "assert"; $b = $_P ...

  2. 获取网页js代码的一个方法

    这个是看了别人的代码,稍加修改而成的.怕时间长忘了,在这里记一笔: console.log(require(["foo:bar.js"]).prototype.someMethod ...

  3. git操作练习

    github账号注册很久了,使用过idea的版本管理,但是命令行还不会.正好新写了一个小项目,练习一下如何上传到github. @ 目录 新建github仓库 安装git 上传项目 1.创建本地版本库 ...

  4. 关于Redis的工作模式

    1.哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master. 缺点 ...

  5. (转)文件上传org.apache.tomcat.util.http.fileupload.FileUploadException: Stream closed

    文件上传时,tomcat报错org.springframework.web.multipart.MultipartException: Failed to parse multipart servle ...

  6. Kubernetes 多集群在开源项目 KubeSphere 的应用

    Kubernetes 多集群使用场景 随着容器的普及和 Kubernetes 的日渐成熟,企业内部运行多个 Kubernetes 集群已变得颇为常见.概括起来,多个集群的使用场景主要有以下几种. 多集 ...

  7. WordCount of Software Engineering

    1.Github项目地址:https://github.com/BayardM/WordCount 2.PSP表格(before): PSP2.1 Personal Software Process ...

  8. Kernel methods on spike train space for neuroscience: a tutorial

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 时序点过程:http://www.tensorinfinity.com/paper_154.html Abstract 在过去的十年中,人 ...

  9. Federated Learning with Matched Averaging

    挖个坑吧,督促自己仔细看一遍论文(ICLR 2020),看看自己什么时候也能中上那么一篇(流口水)~ 郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Abstract 联邦学习允许边缘设 ...

  10. Java——注解

    注解的产生背景以前,xml以低耦合的方式得到了广大开发者的青睐,xml在当时基本上能完成框架中的所有配置.但是随着项目越来越庞大,xml的配置也越来越复杂,维护性也随之降低,维护成本增高.于是就产生了 ...