目的,在每次请求的时候,对每次传的参数,进行解析,验签,并返回解码后的参数, 以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;
    }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

随机推荐

  1. Python_Day_04 set方法总结

    set(集合) 直接创建一个空集合 set_empty = set() print(set_empty) # set() 根据参数创建 # 根据参数 set_argument = set(42,',' ...

  2. Volley自定义Request及使用单例封装RequestQueue

    一.自定义Request Volley的所有的请求的超类型是Resuest,所有我们常用的请求都是这个类的子类,那么我们自定义View肯定也是基于这个类的. 案例: package com.zhy.v ...

  3. paper 57 :颜色直方图的代码

    clear clc close all Image = imread('29.jpg');[M,N,O] = size(Image);[h,s,v] = rgb2hsv(Image); H = h;  ...

  4. 对于GLM的理解,与方差分析的对比

    最近遇到一个问题,如果因变量为一个连续变量(如胰岛素水平),主要考察的变量为分组变量(如正常血糖组,前糖尿病组,糖尿病组三组),现在的目的是想看调整多种变量(包括多个连续性变量和分类变量)后,胰岛素水 ...

  5. 模拟new实例化对象。

    使用new和字面量的的方法是两种主流创建对象的方法,两种最终都能达到同样的实例化的对象,本章主要围绕new关键字来实例化一个对象并且讲一个不使用new但是完全与new实例化对象相同的例子. 在使用ne ...

  6. [趣味]WhirlPolygon——彩色旋转正多边形

    此程序用于在AutoCAD中以直线绘制彩色旋转正多边形供欣赏~ 此程序附属MagicTable(可到依云官网下载:http://www.yiyunsoftware.com/),安装之即可使用该程序. ...

  7. Struts加入拦截器后取不到页面参数

    在Struts2的demo项目中添加了一个简单的拦截器,突然发现,Action中取不到页面的参数了 这也是很蛋疼的事情,还好这个比较简单,稍微一查就发现问题: Struts2中很多的功能是用拦截器实现 ...

  8. tensorflow world language model

    上文提到了pytorch里的world language model,那么怎么能不说tensorflow的实现呢,还是以tensorflow ptb的代码为例说说. 地址: https://githu ...

  9. codeforces 568a//Primes or Palindromes?// Codeforces Round #315 (Div. 1)

    题意:求使pi(n)*q<=rub(n)*p成立的最大的n. 先收集所有的质数和回文数.质数好搜集.回文数奇回文就0-9的数字,然后在头尾添加一个数.在x前后加a,就是x*10+a+a*pow( ...

  10. deque!

    deque:双端队列 比较常用的函数: que.back() 返回容器que的最后一个元素的引用.如果que为空,则该操作未定义. que.begin() 传回迭代器中的第一个数据地址. que.cl ...