范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

RSAAES实现接口验签和参数加密

  RSA非对称加密
  RSA是一种常用的非对称加密算法,加密和加密使用不同的密钥,常用于要求安全性较高的加密场景,比如接口的验签和接口数据的加密与解密。与非对称加密算法对比,其安全性较高,但是加密性能却比较低,不适合高并发场景,一般只加密少量的数据。AES对称加密
  AES是一种最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的),加密和解密使用的是相同的密钥。其加密性能好,加密解密速度非常快,内存需求低,适用于经常发送数据的场合。RSA+AES实现接口验签和请求参数的加密与解密
  背景:做为程序猿,我们经常需要在我们自己开发的系统上,开发一些接口供第三方调用,那么这个时候,对我们接口的安全性要求就比较高了,尤其是那种需要传输比较私密的信息的时候,其对安全性的要求就更高了。实现思路
  调用方:使用AES对称加密算法对业务请求参数进行加密后传输使用RSA非对称加密算法对AES的密钥进行公钥加密后传输使用RSA的私钥对请求参数进行签名
  接收方:获取到请求参数后,对参数进行验签和业务参数的解密
  问题:为什么要对AES的密钥进行RSA公钥加密后传输?
  AES是对称加密算法,加密和解密的密钥都是同一个,为了防止被别人恶意获取到该密钥,然后对我们的业务请求参数进行解密,我们需要将AES密钥进行非对称加密后再进行传输。代码实现                              org.bouncycastle             bcpkix-jdk15on             1.56                                         com.fasterxml.jackson.core             jackson-databind             2.9.8                                        org.codehaus.jackson             jackson-core-asl             1.8.3         请求和接收的实体对象@Data public class JsonRequest {     //接口id 可空     private String serviceId;     //请求唯一id 非空     private String requestId;     //商户id 非空     private String appId;     //参数签名 非空     private String sign;     //对称加密key 非空     private String aseKey;     //时间戳,精确到毫秒 非空     private long timestamp;     //请求的业务参数(AES加密后传入) 可空     private String body; }serviceId:服务id(接口id)。接口设计分为两种,一种是所有的调用方针对类似的业务,都调用的是同一接口地址,然后内部系统根据serviceId去判断具体是要调用哪个业务方法;另一种是针对不同的调用方,开发不同的接口,接口地址也是不一样,那么这个时候就可以不要serviceId这个字段。本章是使用第二种方式,所以serviceId可以不要(可空)。requestId:请求唯一id。方便查询定位某个请求和防止同个请求多次调用。appId:商户id,即我们会给调用方分配一个这样的id,并且将这个id与调用方的信息进行关联,比如"通过appId查询出调用方的加密密钥等"aseKey:是AES对称加密的密钥。用于解密业务请求参数。这里要先用RSA公钥对aseKey进行加密后传输。timestamp:请求时间戳。可以用该字段,对请求的时间合法性进行校验。(如:过滤掉请求时间不在当前时间的正负10分钟范围内的请求)body:请求的业务参数。对请求的业务参数AES加密后再赋值。AES工具类 public class AESUtil {      /**      * 加密      * @param content 加密文本      * @param key 加密密钥,appSecret的前16位      * @param iv 初始化向量,appSecret的后16位      * @return      * @throws Exception      */     public static String encrypt(String content, String key, String iv) throws Exception {         byte[] raw = key.getBytes();         SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //"算法/模式/补码方式"         IvParameterSpec ivParam = new IvParameterSpec(iv.getBytes()); //使用CBC模式,需要一个向量iv,可增加加密算法的强度         cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParam);         byte[] encrypted = cipher.doFinal(content.getBytes());          return new BASE64Encoder().encode(encrypted);     }      public static String decrypt(String content, String key, String iv) throws Exception {         byte[] raw = key.getBytes();         SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding "); //"算法/模式/补码方式"         IvParameterSpec ivParam = new IvParameterSpec(iv.getBytes()); //使用CBC模式,需要一个向量iv,可增加加密算法的强度         cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParam);         byte[] encrypted = new BASE64Decoder().decodeBuffer(content); //先用base64解密         byte[] original = cipher.doFinal(encrypted);         return new String(original);     }      public static void main(String[] args) throws Exception {         String encrypt = AESUtil.encrypt("你好啊!!!", "1234567890123456", "1234567890123456");         String decrypt = AESUtil.decrypt(encrypt, "1234567890123456", "1234567890123456");         System.out.println(decrypt);     }  RSA工具类 public class RSAUtil {      /**      * 定义加密方式      */     private final static String KEY_RSA = "RSA";     /**      * 定义签名算法      */     private final static String KEY_RSA_SIGNATURE = "MD5withRSA";     /**      * 定义公钥算法      */     private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";     /**      * 定义私钥算法      */     private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";      static {         Security.addProvider(new BouncyCastleProvider());     }      /**      * 初始化密钥      */     public static Map init() {         Map map = null;         try {             KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_RSA);             generator.initialize(2048);             KeyPair keyPair = generator.generateKeyPair();             // 公钥             RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();             // 私钥             RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();             // 将密钥封装为map             map = new HashMap<>();             map.put(KEY_RSA_PUBLICKEY, publicKey);             map.put(KEY_RSA_PRIVATEKEY, privateKey);         } catch (NoSuchAlgorithmException e) {             e.printStackTrace();         }         return map;     }      /**      * 公钥加密      *      * @param data 待加密数据      * @param key  公钥      */     public static byte[] encryptByPublicKey(String data, String key) {         byte[] result = null;         try {             byte[] bytes = decryptBase64(key);             // 取得公钥             X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);             KeyFactory factory = KeyFactory.getInstance(KEY_RSA);             PublicKey publicKey = factory.generatePublic(keySpec);             // 对数据加密             Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");              cipher.init(Cipher.ENCRYPT_MODE, publicKey);             byte[] encode = cipher.doFinal(data.getBytes());             // 再进行Base64加密             result = Base64.encode(encode);         } catch (Exception e) {             e.printStackTrace();         }         return result;     }      /**      * 私钥解密      *      * @param data 加密数据      * @param key  私钥      */     public static String decryptByPrivateKey(byte[] data, String key) {         String result = null;         try {             // 对私钥解密             byte[] bytes = decryptBase64(key);             // 取得私钥             PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);             KeyFactory factory = KeyFactory.getInstance(KEY_RSA);             PrivateKey privateKey = factory.generatePrivate(keySpec);             // 对数据解密             Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");             cipher.init(Cipher.DECRYPT_MODE, privateKey);             // 先Base64解密             byte[] decoded = Base64.decode(data);             result = new String(cipher.doFinal(decoded));         } catch (Exception e) {             e.printStackTrace();         }         return result;     }       /**      * 获取公钥      */     public static String getPublicKey(Map map) {         String str = "";         try {             Key key = (Key) map.get(KEY_RSA_PUBLICKEY);             str = encryptBase64(key.getEncoded());         } catch (Exception e) {             e.printStackTrace();         }         return str;     }      /**      * 获取私钥      */     public static String getPrivateKey(Map map) {         String str = "";         try {             Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);             str = encryptBase64(key.getEncoded());         } catch (Exception e) {             e.printStackTrace();         }         return str;     }      /**      * 用私钥对信息生成数字签名      *      * @param data       加密数据      * @param privateKey 私钥      */     public static String sign(byte[] data, String privateKey) {         String str = "";         try {             // 解密由base64编码的私钥             byte[] bytes = decryptBase64(privateKey);             // 构造PKCS8EncodedKeySpec对象             PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);             // 指定的加密算法             KeyFactory factory = KeyFactory.getInstance(KEY_RSA);             // 取私钥对象             PrivateKey key = factory.generatePrivate(pkcs);             // 用私钥对信息生成数字签名             Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);             signature.initSign(key);             signature.update(data);             str = encryptBase64(signature.sign());         } catch (Exception e) {             e.printStackTrace();         }         return str;     }      /**      * 校验数字签名      *      * @param data      加密数据      * @param publicKey 公钥      * @param sign      数字签名      * @return 校验成功返回true,失败返回false      */     public static boolean verify(byte[] data, String publicKey, String sign) {         boolean flag = false;         try {             // 解密由base64编码的公钥             byte[] bytes = decryptBase64(publicKey);             // 构造X509EncodedKeySpec对象             X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);             // 指定的加密算法             KeyFactory factory = KeyFactory.getInstance(KEY_RSA);             // 取公钥对象             PublicKey key = factory.generatePublic(keySpec);             // 用公钥验证数字签名             Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);             signature.initVerify(key);             signature.update(data);             flag = signature.verify(decryptBase64(sign));         } catch (Exception e) {             e.printStackTrace();         }         return flag;     }       /**      * BASE64 解密      *      * @param key 需要解密的字符串      * @return 字节数组      */     public static byte[] decryptBase64(String key) throws Exception {         return Base64.decode(key);     }      /**      * BASE64 加密      *      * @param key 需要加密的字节数组      * @return 字符串      */     public static String encryptBase64(byte[] key) throws Exception {         return new String(Base64.encode(key));     }      /**      * 按照红黑树(Red-Black tree)的 NavigableMap 实现      * 按照字母大小排序      */     public static Map sort(Map map) {         if (map == null) {             return null;         }         Map result = new TreeMap<>((Comparator) (o1, o2) -> {             return o1.compareTo(o2);         });         result.putAll(map);         return result;     }      /**      * 组合参数      *      * @param map      * @return 如:key1Value1Key2Value2....      */     public static String groupStringParam(Map map) {         if (map == null) {             return null;         }         StringBuffer sb = new StringBuffer();         for (Map.Entry item : map.entrySet()) {             if (item.getValue() != null) {                 sb.append(item.getKey());                 if (item.getValue() instanceof List) {                     sb.append(JSON.toJSONString(item.getValue()));                 } else {                     sb.append(item.getValue());                 }             }         }         return sb.toString();     }      /**      * bean转map      * @param obj      * @return      */     public static Map bean2Map(Object obj) {         if (obj == null) {             return null;         }         Map map = new HashMap<>();         try {             BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());             PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();             for (PropertyDescriptor property : propertyDescriptors) {                 String key = property.getName();                 // 过滤class属性                 if (!key.equals("class")) {                     // 得到property对应的getter方法                     Method getter = property.getReadMethod();                     Object value = getter.invoke(obj);                     if (StringUtils.isEmpty(value)) {                         continue;                     }                     map.put(key, value);                 }             }         } catch (Exception e) {             e.printStackTrace();         }         return map;     }          /**      * 按照红黑树(Red-Black tree)的 NavigableMap 实现      * 按照字母大小排序      */     public static Map sort(Map map) {         if (map == null) {             return null;         }         Map result = new TreeMap<>((Comparator) (o1, o2) -> {             return o1.compareTo(o2);         });         result.putAll(map);         return result;     }      /**      * 组合参数      *      * @param map      * @return 如:key1Value1Key2Value2....      */     public static String groupStringParam(Map map) {         if (map == null) {             return null;         }         StringBuffer sb = new StringBuffer();         for (Map.Entry item : map.entrySet()) {             if (item.getValue() != null) {                 sb.append(item.getKey());                 if (item.getValue() instanceof List) {                     sb.append(JSON.toJSONString(item.getValue()));                 } else {                     sb.append(item.getValue());                 }             }         }         return sb.toString();     }      /**      * bean转map      * @param obj      * @return      */     public static Map bean2Map(Object obj) {         if (obj == null) {             return null;         }         Map map = new HashMap<>();         try {             BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());             PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();             for (PropertyDescriptor property : propertyDescriptors) {                 String key = property.getName();                 // 过滤class属性                 if (!key.equals("class")) {                     // 得到property对应的getter方法                     Method getter = property.getReadMethod();                     Object value = getter.invoke(obj);                     if (StringUtils.isEmpty(value)) {                         continue;                     }                     map.put(key, value);                 }             }         } catch (Exception e) {             e.printStackTrace();         }         return map;     }       public static void main(String[] args) throws Exception {         System.out.println("公钥加密======私钥解密");         String str = "Longer程序员";         byte[] enStr = RSAUtil.encryptByPublicKey(str, publicKey);         String miyaoStr = HexUtils.bytesToHexString(enStr);         byte[] bytes = HexUtils.hexStringToBytes(miyaoStr);         String decStr = RSAUtil.decryptByPrivateKey(bytes, privateKey);         System.out.println("加密前:" + str + " r解密后:" + decStr);          System.out.println(" r");         System.out.println("私钥签名======公钥验证");         String sign = RSAUtil.sign(str.getBytes(), privateKey);         System.out.println("签名: r" + sign);         boolean flag = RSAUtil.verify(str.getBytes(), publicKey, sign);         System.out.println("验签结果: r" + flag);     }  jackson工具类 import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.codehaus.jackson.type.TypeReference; import org.springframework.util.StringUtils;  import java.io.IOException; import java.util.ArrayList; import java.util.List;    @Slf4j public class JacksonUtil {     private static ObjectMapper objectMapper = new ObjectMapper();      /**      * 对象转换成json      *      * @param obj      * @param       * @return      */     public static  String beanToJson(T obj) {         if (obj == null) {             return null;         }         try {             return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);         } catch (Exception e) {             log.error("beanToJson error", e);             e.printStackTrace();             return null;         }     }      /**      * 将JSON字符串根据指定的Class反序列化成Java对象。      *      * @param json      JSON字符串      * @param pojoClass Java对象Class      * @return 反序列化生成的Java对象      * @throws Exception 如果反序列化过程中发生错误,将抛出异常      */     public static Object decode(String json, Class<?> pojoClass)             throws Exception {         try {             return objectMapper.readValue(json, pojoClass);         } catch (Exception e) {             throw e;         }     }      /**      * 将JSON字符串根据指定的Class反序列化成Java对象。      *      * @param json      JSON字符串      * @param reference 类型引用      * @return 反序列化生成的Java对象      * @throws Exception 如果反序列化过程中发生错误,将抛出异常      */     public static Object decode(String json, TypeReference<?> reference) throws Exception {         try {             return objectMapper.readValue(json, reference.getClass());         } catch (Exception e) {             throw e;         }     }      /**      * 将Java对象序列化成JSON字符串。      *      * @param obj 待序列化生成JSON字符串的Java对象      * @return JSON字符串      * @throws Exception 如果序列化过程中发生错误,将抛出异常      */     public static String encode(Object obj) throws Exception {         try {             return objectMapper.writeValueAsString(obj);         } catch (Exception e) {             throw e;         }     }      /**      * 对象转换成格式化的json      *      * @param obj      * @param       * @return      */     public static  String beanToJsonPretty(T obj) {         if (obj == null) {             return null;         }         try {             return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);         } catch (Exception e) {             log.error("beanToJsonPretty error", e);             e.printStackTrace();             return null;         }     }       /**      * 将json转换成对象Class      *      * @param str      * @param clazz      * @param       * @return      */     public static  T jsonToBean(String str, Class clazz) {         if (StringUtils.isEmpty(str) || clazz == null) {             return null;         }         try {             return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);         } catch (Exception e) {             log.error("jsonToBean error", e);             e.printStackTrace();             return null;         }     }      /**      * 将json转换为对象集合      *      * @param str      * @param clazz      * @param       * @return      */     public static  List jsonToBeanList(String str, Class clazz) {         if (StringUtils.isEmpty(str) || clazz == null) {             return null;         }         JavaType javaType = getCollectionType(ArrayList.class, clazz);         try {             return objectMapper.readValue(str, javaType);         } catch (IOException e) {             log.error("jsonToBeanList error", e);             e.printStackTrace();             return null;         }     }     public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {         return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);     } }  加密解密验签名流程演示 public class RequestTest {     public static void main(String[] args) {         /****先给调用方分配一组RSA密钥和一个appId****/         //初始化RSA密钥         Map init = RSAUtil.init();         //私钥         String privateKey = RSAUtil.getPrivateKey(init);         //公钥         String publicKey = RSAUtil.getPublicKey(init);         //appId,32位的uuid         String appId = getUUID32();         /****先给调用方分配一组RSA密钥和一个appId****/          /*****调用方(请求方)*****/         //业务参数         Map  businessParams = new HashMap<>();         businessParams.put("name","Longer");         businessParams.put("job","程序猿");         businessParams.put("hobby","打篮球");          JsonRequest jsonRequest = new JsonRequest();         jsonRequest.setRequestId(getUUID32());         jsonRequest.setAppId(appId);         jsonRequest.setTimestamp(System.currentTimeMillis());         //使用appId的前16位作为AES密钥,并对密钥进行rsa公钥加密         String aseKey = appId.substring(0, 16);         byte[] enStr = RSAUtil.encryptByPublicKey(aseKey, publicKey);         String aseKeyStr = HexUtils.bytesToHexString(enStr);         jsonRequest.setAseKey(aseKeyStr);         //请求的业务参数进行加密         String body = "";         try {             body = AESUtil.encrypt(JacksonUtil.beanToJson(businessParams), aseKey, appId.substring(16));         } catch (Exception e) {             throw new RuntimeException("报文加密异常", e);         }         jsonRequest.setBody(body);         //签名         Map paramMap = RSAUtil.bean2Map(jsonRequest);         paramMap.remove("sign");         // 参数排序         Map sortedMap = RSAUtil.sort(paramMap);         // 拼接参数:key1Value1key2Value2         String urlParams = RSAUtil.groupStringParam(sortedMap);         //私钥签名         String sign = RSAUtil.sign(HexUtils.hexStringToBytes(urlParams), privateKey);         jsonRequest.setSign(sign);          /*****调用方(请求方)*****/          /*****接收方(自己的系统)*****/         //参数判空(略)         //appId校验(略)         //本条请求的合法性校验《唯一不重复请求;时间合理》(略)         //验签         Map paramMap2 = RSAUtil.bean2Map(jsonRequest);         paramMap2.remove("sign");         //参数排序         Map sortedMap2 = RSAUtil.sort(paramMap2);         //拼接参数:key1Value1key2Value2         String urlParams2 = RSAUtil.groupStringParam(sortedMap2);         //签名验证         boolean verify = RSAUtil.verify(HexUtils.hexStringToBytes(urlParams2), publicKey, jsonRequest.getSign());         if (!verify) {             throw new RuntimeException("签名验证失败");         }         //私钥解密,获取aseKey         String aseKey2 = RSAUtil.decryptByPrivateKey(HexUtils.hexStringToBytes(jsonRequest.getAseKey()), privateKey);         if (!StringUtils.isEmpty(jsonRequest.getBody())) {             // 解密请求报文             String requestBody = "";             try {                 requestBody = AESUtil.decrypt(jsonRequest.getBody(), aseKey, jsonRequest.getAppId().substring(16));             } catch (Exception e) {                 throw new RuntimeException("请求参数解密异常");             }             System.out.println("业务参数解密结果:"+requestBody);         }         /*****接收方(自己的系统)*****/     }      public static String getUUID32() {         String uuid = UUID.randomUUID().toString();         uuid = uuid.replace("-", "");         return uuid;     } }
  执行结果:业务参数解密结果:{"name":"Longer","job":"程序猿","hobby":"打篮球"}
  到此,调用方要做的和接收方做的其实都已经清楚了。
  调用方:1.业务参数进行AES对称加密2.AES密钥进行RSA非对称加密3.使用RSA生成签名
  接收方:验证签名AES密钥解密业务参数解密请求参数的统一处理
  上面讲到,我们接受的请求对象是JsonRequst对象,里面除了body成员变量是跟业务相关,其他成员变量(sericeId,appId等)都是与业务不相关的。那么,如果我们在controller层用JsonRequest对象去接收请求参数的话,其实是不那么规范的。
  那么我们能不能对请求参数进行统一处理,使得传到controller层的参数只是跟业务相关的参数,并且在controller层也无需关注加密解密和验签的东西。实现方法:使用过滤器,拦截请求,并对请求参数进行统一处理(加密解密,验签等)自定义request对象(新增类继承HttpServletRequestWrapper类),对请求参数进行过滤处理,使得controller只接受业务参数。
  问题:为什么需要自定义request对象?
  因为获取post请求传递的json对象,需要用request对象流取获取,而一旦我们调用了request.getInputStream()方法后,流将会自动关闭,那么到了我们的controller层就不能再获取到请求参数了。自定义request对象import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.Charset;   public class RequestWrapper extends HttpServletRequestWrapper {     private byte[] body;      public RequestWrapper(HttpServletRequest request) throws IOException {         super(request);         String sessionStream = getBodyString(request);         body = sessionStream.getBytes(Charset.forName("UTF-8"));     }      public String getBodyString() {         return new String(body, Charset.forName("UTF-8"));     }      public void setBodyString(byte[] bodyByte){         body = bodyByte;     }      /**      * 获取请求Body      *      * @param request      * @return      */     public String getBodyString(final ServletRequest request) {         StringBuilder sb = new StringBuilder();         InputStream inputStream = null;         BufferedReader reader = null;         try {             inputStream = cloneInputStream(request.getInputStream());             reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));             String line = "";             while ((line = reader.readLine()) != null) {                 sb.append(line);             }         } catch (IOException e) {             e.printStackTrace();         } finally {             if (inputStream != null) {                 try {                     inputStream.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }             if (reader != null) {                 try {                     reader.close();                 } catch (IOException e) {                     e.printStackTrace();                 }             }         }         return sb.toString();     }      /**      * Description: 复制输入流
* * @param inputStream * @return
*/ public InputStream cloneInputStream(ServletInputStream inputStream) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { e.printStackTrace(); } InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); return byteArrayInputStream; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }自定义过滤器 @Slf4j public class OutRequestFilter extends OncePerRequestFilter { @SneakyThrows @Override protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException { RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); HttpServletRequest request = (HttpServletRequest) servletRequest; String requestURL = request.getRequestURI(); log.info("请求路径:" + requestURL); String method = request.getMethod(); if (!"POST".equals(method)) { throw new RuntimeException("暂不支持" + method + "请求方式"); } //获取请求参数 RequestWrapper requestWrapper = new RequestWrapper(request); String bodyString = requestWrapper.getBodyString(); if (StringUtils.isEmpty(bodyString)) { throw new RuntimeException("请求体不能为空"); } log.info("请求参数:" + bodyString); JsonRequest jsonRequest = JacksonUtil.jsonToBean(bodyString, JsonRequest.class); //step0 参数合法性校验(非空判断等) parameterValidate(jsonRequest); //step1 判断请求合法性。1.不允许重复请求(通过请求唯一id判断)2.不允许请求时间与当前时间差距过大(正负10分钟) long currentTime = System.currentTimeMillis(); long subTime = currentTime - jsonRequest.getTimestamp(); long tenMinuteMs = 10 * 60 * 1000; if (subTime < -tenMinuteMs || subTime > tenMinuteMs) { throw new RuntimeException("请求异常,请求时间异常"); } String requestUUIDKey = MessageFormat.format(RedisConstant.REQUEST_UUID, jsonRequest.getRequestId()); Object requestFlag = redisUtil.get(requestUUIDKey); if (!StringUtils.isEmpty(requestFlag)) { throw new RuntimeException("请求异常,重复的请求"); } redisUtil.set(requestUUIDKey, JacksonUtil.beanToJson(jsonRequest), 15 * 60); //step2 参数解密,签名校验,参数过滤和传递 Map paramMap = RSAUtil.bean2Map(jsonRequest); paramMap.remove("sign"); //根据appkey获取rsa密钥 String appIdKey = MessageFormat.format(RedisConstant.REQUEST_APPID, jsonRequest.getAppId()); Object ob = redisUtil.get(appIdKey); if (StringUtils.isEmpty(ob)) { throw new RuntimeException("找不到指定的appid"); } String jsonString = (String) ob; JSONObject jsonObject = JSONObject.parseObject(jsonString); //rsa公钥 String publicKey = jsonObject.getString("publicKey"); //rsa私钥 String privateKey = jsonObject.getString("privateKey"); //参数排序 Map sortedMap = RSAUtil.sort(paramMap); //拼接参数:key1Value1key2Value2 String urlParams = RSAUtil.groupStringParam(sortedMap); //签名验证 boolean verify = RSAUtil.verify(HexUtils.hexStringToBytes(urlParams), publicKey, jsonRequest.getSign()); if (!verify) { throw new RuntimeException("签名验证失败"); } //私钥解密,获取aseKey String aseKey = RSAUtil.decryptByPrivateKey(HexUtils.hexStringToBytes(jsonRequest.getAseKey()), privateKey); if (!StringUtils.isEmpty(jsonRequest.getBody())) { // 解密请求报文 String body = ""; try { body = AESUtil.decrypt(jsonRequest.getBody(), aseKey, jsonRequest.getAppId().substring(16)); } catch (Exception e) { log.error("请求参数解密异常:",e); throw new RuntimeException("请求参数解密异常"); } //报文传递至controller层 requestWrapper.setBodyString(body.getBytes(Charset.forName("UTF-8"))); } //将request传递下去 filterChain.doFilter(requestWrapper, servletResponse); } private void parameterValidate(JsonRequest jsonRequest) { if (StringUtils.isEmpty(jsonRequest.getAppId())) { throw new RuntimeException("参数异常,appId不能为空"); } if (StringUtils.isEmpty(jsonRequest.getAseKey())) { throw new RuntimeException("参数异常,aseKey不能为空"); } if (StringUtils.isEmpty(jsonRequest.getRequestId())) { throw new RuntimeException("参数异常,requestId不能为空"); } if (StringUtils.isEmpty(jsonRequest.getSign())) { throw new RuntimeException("参数异常,sign不能为空"); } if (jsonRequest.getTimestamp() == 0l) { throw new RuntimeException("参数异常,timestamp不能为空"); } } }完整流程演示调用方HttpClientUtils类 public class HttpClientUtils { private static Logger logger = Logger.getLogger(HttpClientUtils.class.getName()); public static String doPostJson(String url, String json) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { resultString = e.getMessage(); logger.info("http访问失败:" + e); } finally { try { response.close(); } catch (IOException e) { logger.info("response关闭失败:" + e); } } return resultString; } /** * post请求,签名和报文加密 * * @param url 请求地址 * @param json 请求json参数 * @param appId 商户id * @param publicKey rsa公钥 * @param privateKey rsa私钥 * @return */ public static String doPostJsonForSign(String url, String json, String appId, String publicKey, String privateKey) { String aseKey = appId.substring(0, 16); JsonRequest jsonRequest = new JsonRequest(); jsonRequest.setRequestId(getUUID32()); jsonRequest.setAppId(appId); jsonRequest.setTimestamp(System.currentTimeMillis()); //aseKey 加密 logger.info("开始aseKey加密...."); byte[] enStr = RSAUtil.encryptByPublicKey(aseKey, publicKey); String aseKeyStr = HexUtils.bytesToHexString(enStr); jsonRequest.setAseKey(aseKeyStr); //请求参数进行加密 String body = ""; try { logger.info("开始请求参数加密...."); body = AESUtil.encrypt(json, aseKey, appId.substring(16)); } catch (Exception e) { logger.info("报文加密异常:" + e); throw new UncheckedException("报文加密异常", e); } jsonRequest.setBody(body); Map paramMap = RSAUtil.bean2Map(jsonRequest); paramMap.remove("sign"); // 参数排序 Map sortedMap = RSAUtil.sort(paramMap); // 拼接参数:key1Value1key2Value2 String urlParams = RSAUtil.groupStringParam(sortedMap); //私钥签名 logger.info("开始参数签名...."); String sign = RSAUtil.sign(HexUtils.hexStringToBytes(urlParams), privateKey); jsonRequest.setSign(sign); String requestParams = JacksonUtil.beanToJson(jsonRequest); logger.info("发起请求...."); String result = doPostJson(url, requestParams); return result; } public static String getUUID32() { String uuid = UUID.randomUUID().toString(); uuid = uuid.replace("-", ""); return uuid; } } 需要传递的业务参数对象@Data public class RequestDto { private String name; private int age; private String hobby; }发送请求 public static void main(String[] args) { //请求地址 String url = "http://127.0.0.1:8888/test"; RequestDto requestDto = new RequestDto(); requestDto.setAge(100); requestDto.setName("Longer"); requestDto.setHobby("ball"); String json = JacksonUtil.beanToJson(requestDto); //appId String appId = ""; //rsa公钥 String publicKey = ""; //rsa私钥 String privateKey = ""; HttpClientUtils.doPostJsonForSign(url, json, appId, publicKey, privateKey) }接收方controller@Slf4j @RestController public class TestController { @RequestMapping("test") public String test(RequestDto requestDto){ log.info("接收到的请求参数为:"+ JacksonUtil.beanToJson(requestDto)); return "a"; } }   因为我们对参数进行了统一处理,所以我们的controller接收参数的对象是RequestDto对象,而不是JsonRequest对象   原链接:https://www.jianshu.com/p/9061da5e25d1

TheShy回来后WBG无敌了!焕烽直播改口狂喜,辅助ON打上韩服第七前言要说转会期过后哪支战队最令人关注,那必须得是有着TheShy的WBG战队。就在今年,外媒LeagueonLock对LPL第617名队伍进行了排名预测(前六暂未做排名),第七名就复古传奇有特效的传奇是有多好玩?随时变大随时喷火随时招宝宝玩了传奇这么多年基本上模式都是差不多的打怪换装备然后继续打怪换装备每个装备都是一样的捡到了也没有那么大的惊喜了有特效的传奇还是第一次玩感觉特效很多每个都可以试试什么效果还是挺有意思AG参与复活赛,究竟谁能夺得胜利2021KPL秋季赛即将结束,这次秋季赛让我们看到了XYG从B组杀进了四强,也看到了2个七胜的大魔王Ester,总决赛将在12月25号进行比拼,在在20号到24号这段时间内是没有比完美世界魔女换新颜,仙女清漪首次亮相,拍卖红衣女酷似火灵儿在完美世界动画中,大魔王石中天正在大闹武王府,给自己孙子石昊报仇,武王已经败了,雨王还在送人头,霸气外漏,可谓真正的热血沸腾。只是大魔王打完巅峰赛之后,石昊重新营业,开启全新的副本暗黑传声筒又加零,这是暗黑4还是暗黑3。5?翻译雪暴君在翻完12月更新博文之后,我去官网论坛和米国贴吧逛了一圈,出乎意料的是,大家没有在讨论牛逼闪闪的技能效果,而基本都在喷文章基本没有提及的物品系统,不得不感叹暗黑2玩家社区楼兰精英版落地大概多少钱?谢谢邀请,很高兴回答你的问题。楼兰定位于东风日产旗下一款中型SUV,目前在售的精英版是搭载2。5L自吸发动机的2017款2。5LXE两驱精英版,厂商指导价是23。88万元。综合各地拘留所只有韩涵在榜,看到巅峰赛排名后,韩涵对吕德华刮目相看在最近S25赛季已经濒临尾声,多家公会都想要在这个赛季取得好成绩,但神鸡营的横空出世却是让他们的计划完全落空。巅峰赛前十中多个神鸡营的选手存在,基本就已经确定神鸡营在这个赛季的巅峰为什么网络游戏多开和代练需要IP代理改IP地址?面对如今五花八门跨平台的网游游戏(端游手游),玩家们想要在不同操作系统中玩好玩明白这些游戏其实是挺难的,也非常的不方便经常需要更换不同的设备进行游戏的游玩实属不便,直到(游戏)安卓双城传说上分阵容保镖狙神烬,黑白魔法的克星?大家好,我是墨语白书,当前热门阵容,黑白法师阵容,那么有什么阵容能够克制这个阵容呢?今天小编就为大家带来了一套阵容保镖狙神烬,能够有效克制这套阵容,下面就一起去看看吧!阵容非常的简梦之队锤爆WE大破WB,接连战胜两支职业队,KPL到底怎么了?王者荣耀KPL的秋季赛已经进行到了季后赛的关键阶段,剩下的几支队伍无一例外都是拥有很强的实力,也都处于KPL的上游水平。而对于一些提前遭到淘汰的队伍来说,他们在很长的一段时间内将没怀旧Retroarch最新版本1。9。14发布每天进步一点点加油()各位小伙伴们,大家好!希望我的分享能伴随着大家每天进步一点点!Retroarch游戏机万能模拟器Retroarch最新版本1。9。14发布了!灵光一闪各位小伙
今日谷雨丨播种美好与幸福,且歌且行谷雨GrainRain时节雨生百谷夏将至杨花柳絮随风舞草木微摇谷雨时节酒拨嫩醅倾绿液,曲调新谱促朱弦春色浓深时,雨便越发多了起来预示着春天接近尾声的谷雨节气便在这一片迷迷蒙蒙的春雨五款绝佳的早餐,唤醒一天的元气01Breakfast早餐是一天美好生活的开场白一顿营养全面的早餐会让你元气满满,充满活力几款简单又有营养的早餐菜谱满足你从周一到周五工作日的早餐需求周一蔬菜鸡蛋三明治食材2片全麦卡萨帝每个五年计划,都可圈点本文共550字,读完约需50秒自2006年创立以来,从超越众多海外高端家电品牌,占据高端第一份额,实现0到1的蜕变,到冰箱品类行业第二品牌,完成从1到2的地位延伸,卡萨帝用了15年石家庄联通举办重走红色基地汲取精神力量健步走竞赛活动为庆祝中国共产党成立100周年,深入推进党史学习教育走深走实,5月8日,石家庄联通在红色圣地西柏坡隆重举行重走红色基地汲取精神力量2021年职工健步走竞赛活动,活动路线全长9。8公五一去哪都堵!卡萨帝宅家快乐,应有尽有五一即将到来,漫天飞舞的游玩攻略看得眼花缭乱,各大景区人山人海,去哪儿都堵?假期到底怎么过?卡萨帝为您解答宅家最快乐,您想要的应有尽有!Q1宅之穿衣,哪种穿搭风格最适合自己?点击下520告白季美的再送助攻,为全民美好生活买单520,一年一度的甜蜜季。相信许多有心之人已经提前关注电商信息,拒绝让告白只停留于情话。据悉,为了感恩美粉的一路相伴,美的已经启动全球美粉节,趁520将至,以惊喜优惠福利助力告白季有一种生活智慧,叫刚刚好LIFE2021村上春树说身体是每个人的神殿,不管里面供奉的是什么,都应该好好保持她的强韧美丽和清洁。但是这座神殿的保养却需要精准的把控不然便会不受控,闹情绪一些刚刚好的生活智慧能卡萨帝灵感大片,缔造家的融合艺术理想的家是融合艺术自然与生活人文与科技不同元素的鲜明对比或出众或交融的美感赋予了家不同的气质你理想中的家是什么模样呢?历经近千个日夜的细心打磨融合都市之行与NATURALLIFE的海岛送冰箱得,不偿失!卡萨帝服务得,能偿失往海岛上送冰箱,不仅运输困难,还费时费力,对许多品牌来说得不偿失,但卡萨帝却不这么想。针对青岛市西海岸新区灵山岛四面环海交通不便的情况,卡萨帝服务管家为岛内桑吉尔多民宿老板荆先生定珍惜自然馈赠,留住四季美味大自然总是无比友善慷慨,在四季轮回里,不时地给人类回馈不同的礼物。中国人把应时而食的养生哲学表达的淋漓尽致,顺应时节,享受自然馈赠,才能品味更多季节美味。春笋春笋,该是春天的精灵,首批卡萨帝用户发票留了10年,冰箱用了10年随着时代的发展,发票经历了由手写到机打再到电子的变迁,但不变的是每一张发票的背后都有一位忠实的用户。5月25日,家住陕西的刘先生在卡萨帝生活微信公众号后台晒出了10年前购买的卡萨帝