目的,在每次请求的时候,对每次传的参数,进行解析,验签,并返回解码后的参数, 以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. manjaro与python开发环境配置

    1.manjaro配置 1.1.启动项 sudo update-grub 注:Manjaro(archLinux)系统时间快8小时--> sudo timedatectl set-local-r ...

  2. java.io.IOException: Stream closed 的问题

    public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); ...

  3. Jenkins(1)—— 部署安装

    最近有聊到接口自动化,持续集成这方面,所以想从持续集成工具Jenkins作为切入点来学习一下 一.jenkins概念 Jenkins是一个开源的.可扩展的持续集成.交付.部署(软件/代码的编译.打包. ...

  4. CocosCreator游戏开发(四)实现摇杆控制角色功能

    时隔3年,我又开始继续写这个系列的帖子了,也不知道是会写完全系列,还是再次夭折. 废话不多.直接开始主题了 主要实现的功能点包含这些内容:通过摇杆控制角色进行八方位移动,并按照各方位播放对应移动动画 ...

  5. Reinforcement Learning, Fast and Slow

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 1 DeepMind, London, UK2 University College London, London, UK3 Prince ...

  6. python 复制与粘贴处理笔记

    在python中用有一个模块可以用来处理剪切板复制的内容,pyperclip模块 pyperclip模块有copy()和paste()函数,分别用于向计算机的剪贴板发送文本,或从它接受文本. pype ...

  7. v-html渲染富文本图片宽高问题

    v-html渲染富文本v-html是用来渲染html的节点及字符串的,但是渲染后富文本里的图片宽高会溢出所在div的区域但是使用css直接给img是没有办法设置img的宽高的,需要使用深层级来给img ...

  8. python爬虫-爬取百度图片

    python爬虫-爬取百度图片(转) #!/usr/bin/python# coding=utf-8# 作者 :Y0010026# 创建时间 :2018/12/16 16:16# 文件 :spider ...

  9. TCP/IP网络编程之数据包协议

    一.概要 在了解了网络字节序之后,接下来就是要讲最最重点的消息协议.数据包是什么呢,数据包可以理解为两个人讲电话说的每一句话的内容.通过大家约定好的方式去理解.达到只有接听电话两个人才懂的东西.在程序 ...

  10. Welcome To CUG_YZL's cnblogs

    Welcome To CUG_YZL's cnblogs  My name is YZL, studied in China University of Geosciences Wuhan now.W ...