Http接口安全整理


1.Http接口安全概述:

       1.1、Http接口是互聯網各系統之間對接的重要方式之一,使用http接口,開發和調用都很方便,也是被大量采用的方式,它可以讓不同系統之間實現數據的交換和共享,但由於http接口開放在互聯網上,那么我們就需要有一定的安全措施來保證不能是隨隨便便就可以調用;

       1.2、目前國內互聯網公司主要采用兩種做法實現接口的安全:

                一種是以支付寶等支付公司為代表的私鑰公鑰簽名驗證機制;

                一種是大量互聯網企業都常采用的參數簽名驗證機制;

2. Http接口安全演進:

         2.1.完全開放的接口(完全開放)

         2.2.接口參數簽名(基本安全)

         2.3.接口參數簽名+時效性驗證(更加安全)

         2.4.接口參數私鑰簽名公鑰驗簽(固若金湯)

         2.5.接口參數簽名+Https(金鍾罩)

         2.6.口參數私鑰簽名公鑰驗簽+Https(金鍾罩)

         總之:安全是相對的,只有相對的安全,沒有絕對的安全!

3.Http接口安全設計及應用

         3.1 接口參數私鑰簽名公鑰驗簽

先來看看私鑰+公鑰的安全模式,這是一種更為安全的方式,它通過私鑰和公鑰實現接口的安全,目前互聯網中主要是以支付寶

為代表的公司采用這種機制;(有很多開放平台也是采用這種機制) . 具體業務流所示:

 

 

該簽名是通過4個秘鑰來實現的,分別是:

客戶端應用私鑰 , 客戶端公鑰 , 服務端應用私鑰 , 服務端公鑰.

私鑰都是用來生成簽名的,公鑰都是用來解密的,客戶端的公鑰解密客戶端的私鑰生成的簽名,服務端的公鑰解密服務端的私鑰生

成的簽名,相信這樣解釋應該會比較好理解的.

        好了,下面就來看看是如何具體操作的,我的具體操作步驟:

        首先,4把密鑰都是通過OpenSSL工具生成,你需要先獲得這個工具:官方網站:https://www.openssl.org/

        介紹一下這個工具吧,OpenSSL 是一個開源的安全套接字層密碼庫,囊括主要的密碼算法、常用的密鑰和證書封裝管理功能及SSL協議,並提供豐富的應用程序測試或其它目的使用;OpenSSL整個軟件包大概可以分成三個主要的功能部分:SSL協議庫、應用程序以及密碼算法庫;

        3.1.1確保Linux已經安裝openssl  :  

               使用命令檢查Linux是否已經安裝openssl:yum list installed | grep openssl

               如果沒有安裝,則執行命令進行安裝:yum install openssl openssl-devel -y

 

 3.1.2創建秘鑰生成的目的地(文件夾):

ps : 我是在根目錄中的soft文件夾下創建的

 

 mkdir server , mkdir client 先創建這兩個文件,然后Linux會默認將生成的秘鑰放入其中.

3.1.3 生成秘鑰

 

進入server文件,輸入openssl  進入Openssl命令行;
使用openssl生成私鑰,執行如下命令:
genrsa -out rsa_private_key.pem 2048
注意一點Java開發者需要將私鑰轉換成PKCS8格式,其他語言不用這一步操作,執行如下命令:
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem
使用openssl生成公鑰,執行如下命令:
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
退出openssl命令行:exit
經過以上步驟,我們可以在當前目錄中(server)看到三個文件:
 rsa_private_key.pem(RSA私鑰)
 rsa_private_key_pkcs8.pem(pkcs8格式RSA私鑰)(我們java要使用的私鑰是這一個)
 rsa_public_key.pem(對應RSA公鑰)
         client端的秘鑰與server端的秘鑰生成一樣,這里就不敘述了,想體驗的朋友自己按照上一步操作再做一遍即可.

   

   3.2 Demo

      好了,現在就可以使用生成的秘鑰來做一個簡單的Demo來檢驗一下了.

      首先這里提供一個簽名處理工具類:

      說明:該工具類依賴的包是java.security,該包JDK8才有,所以,如果使用下面這個Demo的話,建議檢查自己的JDK是不是JDK8,如果是JDK8以下的版本,以下的Demo是用不了的.解決方法就是,使用第三方提供的依賴包,效果是相同的,依賴包如下:

 

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>RELEASE</version>
</dependency>

以下是簽名工具類:

package com.kinglong.http.utils;

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
* 簽名處理工具類
*
* @author haojinlong
*
*/
public class MyRSAUtils {

public static final String CHARSET = "utf-8";

/**
* RSA私鑰簽名
*
* @param src 客戶端傳過來的原始參數
* @param priKey 我們的客戶端私鑰
* @return
* @throws Exception
*/
public static String sign (String src, String priKey) {
try {
KeyFactory fac = KeyFactory.getInstance("RSA");
byte[] pribyte = Base64.getDecoder().decode(priKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pribyte);
RSAPrivateKey privateKey = (RSAPrivateKey) fac.generatePrivate(keySpec);

Signature sigEng = Signature.getInstance("SHA1withRSA");
sigEng.initSign(privateKey);
sigEng.update(src.getBytes(MyRSAUtils.CHARSET));

byte[] signature = sigEng.sign();
return Base64.getEncoder().encodeToString(signature);

} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* RSA公鑰驗證簽名
*
* @param src 客戶端穿過來的原始數據
* @param sign 簽名
* @param publicKey 我們的客戶端公鑰
* @return
*/
public static boolean signVerify (String sign, String src, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");

//將公鑰變為一個字節數組
byte[] encodedKey = Base64.getDecoder().decode(publicKey);
//使用秘鑰工廠生成一個公鑰對象pubKey
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

//使用"SHA1WithRSA"算法,生成簽名對象signature
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initVerify(pubKey);
signature.update(src.getBytes(MyRSAUtils.CHARSET));

boolean bverify = signature.verify(Base64.getDecoder().decode(sign));
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
下面是簽名時需要的輔助工具類:

 

package com.kinglong.http.HttpUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
*生成簽名需要的輔助工具類
*/
public class SignUtils {
/**
*將傳進來的無序參數,轉換為有序的字符串輸出
*/
public static String generateSortSign(Map<String,Object>paramMap){

Map<String,Object> treeMap = new TreeMap<String, Object>(paramMap);
Set<Map.Entry<String,Object>> entrySet = treeMap.entrySet();
Iterator<Map.Entry<String,Object>> iterator = entrySet.iterator();
StringBuffer stringBuffer = new StringBuffer();

while (iterator.hasNext()){

Map.Entry<String,Object> entry=iterator.next();
String keys = entry.getKey();
String value = (String) entry.getValue();
stringBuffer.append(keys).append(value);

}
return stringBuffer.toString();

}

}
客戶端代碼:

 

package com.kinglong.http.controller;

import com.alibaba.fastjson.JSONObject;
import com.kinglong.http.HttpUtils.HttpClientUtils;
import com.kinglong.http.HttpUtils.SignUtils;
import com.kinglong.http.constans.Constans;
import com.kinglong.http.utils.MyRSAUtils;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class HttpClient {


public static void main(String[] args) {

demo();

}

public static void demo(){
String url ="http://localhost:8080/api/verifydemo";

//這里僅做演示使用,所以隨手寫了幾個信息
String realName = "我是驗證信息";
String phone = "17000000000";
String idCard = "6403021992120511111";
String bankCard = "5555555555555555555555";

//封裝參數
Map<String,Object> paramMap = new ConcurrentHashMap<String, Object>();
paramMap.put("realName",realName);
paramMap.put("phone",phone);
paramMap.put("idCard",idCard);
paramMap.put("bankCard",bankCard);

//生成客戶端簽名,注意看,客戶端這里生成簽名的時候是使用的客戶端私鑰Constans.CLIENT_PRIVATE_KEY
String sign = MyRSAUtils.sign(SignUtils.generateSortSign(paramMap),Constans.CLIENT_PRIVATE_KEY);
paramMap.put("sign",sign);

//發送請求時注意:因為肯定會不可避免的要輸出中文字符,所以記得在使用HttpClient通信的時候,設置編碼格式為utf-8
//否則,我想你的簽名驗證成功的概率基本等於讓兩條平行線相交成功的概率
String json = HttpClientUtils.doPostByEncode(url,paramMap,"utf-8");

//解析json字符串
JSONObject jsonObject = JSONObject.parseObject(json);
String code = jsonObject.getString("code");
String erroMessage = jsonObject.getString("erroMessage");
JSONObject object = jsonObject.getJSONObject("object");
String result = object.getString("result");
String ret_sign = object.getString("ret_sign");//服務端發回的簽名
String resultDesc = object.getString("resultDesc");

//封裝參數准備進行驗證該返回信息是否是由服務端發回的
//驗證時的參數可以根據自己公司的規范去選擇,我這里就選了這兩個參數,因為偷懶沒有生成新的map,所以這里需要clear一下.
paramMap.clear();
paramMap.put("result",result);
paramMap.put("resultDesc",resultDesc);

//調用簽名驗證工具類進行簽名驗證
//重點!!!敲黑板啦!!注意看,這里使用的是服務端的公鑰來解密服務端發送的簽名的,很多人肯定使用了客戶端公鑰來解密,必然是失敗的
//同理,在服務端要是用客戶端的公鑰解密客戶端發送的簽名,這個前面的流程圖我覺得已經說得很明確了
boolean isTrue = MyRSAUtils.signVerify(ret_sign,SignUtils.generateSortSign(paramMap),Constans.SERVER_PUBLIC_KEY);

if (isTrue){
System.out.println("返回正確信息,可以進行下一步操作");
}else {
System.out.println("返回錯誤信息,不可以進行下一步操作");
}

}
}
 

下面是客戶端使用的公共常量類:

 

 

package com.kinglong.http.constans;

public class Constans {
//客戶端私鑰
public static final String CLIENT_PRIVATE_KEY="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClEAp0NDHtb9w5iJfyNOh6DeCRv0RjGFA1CIQ6ZxpfIc65h03hUGsDjcZtWQQZf7d30hiVCcQLylJYJidHQbPcDWULRhObgUxFIFQ37UW8c8DHDiHPNVRjH47ePi9sZIYbVuaWOJkS9NAoSDTRDA1vS7ewWosA9WyUSEuWSEm6eQQV02nlhf/cIsu3biDmq8Y9ffg9lLgEYbH4VQu6bRXgqpy90OxFh3Jh16nbAZXqAJMKCZyJYo5B9ZN4No8Q/EMZe98DNFybOod7WTuQmrS5FM0vCsjQpczBwn+dny5grWl4YgUYgAWOnvPr2iMXVXLqbQjcVEMFKwr/71K9yVehAgMBAAECggEBAJi6nvGm2gu41SznFrEmA3XsIT66m6yVcqGfn7nqbJxZy84fRBCXOG2xYUkMdJ6jbj+QRu6geqXuLwMhSnbEdIfIXRZxYPMiUFAl+cdF5KDa+iU1DlOMJOkS6j75iyfgW7YwUmvtMrY3j+O17CkB3ex9QxoKrVPVwwHxYv9LI+1FT0Fd3157gIbkBTdXnKUc4O4Z4/FTcvPYNR2h9O79xQbcx0clUbj61yQxkxVyN25plxxAVoUW7mKNleH6nFAkeb/gYxLdwlUHm53cYowSDtbzo4udB6qPWj/PvCVgu7UatnEhcyX9ZKCBmrX3+EvWTw9A9dTDSMu4D9CX2QsUq4ECgYEA2pTWhw6ulzWlORu12WcUxDzCVuKV7dGXCQ2MRnGELLm7HzAmRIjd0LubGWAKwUNa6NhWVNsupI8/hDh3hyjGAiTVn0gowDVQj4s/vIx5aYMsg6nxzUl3KrjBJyORHnY8+fFp5V1SHr0G6jOTynsqX0ONMpNZ/zyxiOQG5vGs7A8CgYEAwVHJAe0Eqz2jwLHA10Afsh3vS2Otcjj1cj9jxz1ZFoYIbD64Mc77bem+9x7xpI4w6/WezUOO4gZ8RaAh7PpyMWbTwzq0QHS7bSzKMfpRALyVf7HyIS/QuTUA0oLZXx9vk4wAe0rZBNFuuEtyU3XgHz8OSY/uHeTXJWYtJHTIkU8CgYAD6YgRcMTVNgOYCxPtKTgo7wF3dqTCVe8DHXf2Rs/b0RM1UrJMpbp6ovD6ukpW/TKiWkTpTeb+0QWNA0m4ZJVusmQUbsEz94BSoWZppIYDynJAhQkr6HW2kQn7/ln5lpouyxBfJ5VxsWZvSK8Lf7rZa6caUaLZu6dd0N8CwS6cJwKBgD74B9RTwtiQXF1wyNKUNX7MF1zkG+P/v5s2IKcOSY13nRi9GTxIIke8ApL2BlnGYxMIz3Am2EyxNhtrvIE3VqjWyJVn8ryoCUDXfQjocygdRUjxyl+a9o7NP/ZR3sIIOEzEJogCakwSd9EZ6iRbWeRzopC9jB86ogWxkXS1gXsrAoGAEOvVsv8lpY+TCs3Q78pnedwFIXXkrOknrXq2gb+WHmSSDCELOaqWsX73rdbW4IcmJ6kT2d5bn6hH+/2Zw/+Xpy6maBeLxxscXBfLHwT/85YG5z21LuyikB7ht/tZCNxQOjEfvo5MdCwNN6GhpPSVwNjLAvtiImJYnlDuGPaG+RU=";

//服務端公鑰
public static final String SERVER_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqlG1v814kyQIEpLQnyxo/4RUku8PE+csGzH073XoW8xPdXAH8C7Y3isyDKSClTEGeS/SaqioYPFI+YlD0Eag1DEoVPkDcJeWsPv4FZr2ZO2wCenwqUrH5BM7ZapSBLenkgdhTF3SIUJMrTfJPaRJM1wb75SPawHO1zueM0cvcbeGNJOyCG69XPhour0Cei/HcflY3bXVx9kKH8vvAmbosAOrIwwdGSnV1YqSlApBmoobGYcaFPv5Clntghq+6P1ut2RSOrKinr0Q4wc4kIpnSY+j2oQ20OZ7rZXuLyGmMSsglvGwgTIoxHqkXpWP3qGFmYHSQgGCMFWxlT1t3e1AawIDAQAB";
}
ok,客戶端就齊活了,下面是服務端:

服務端使用的是springboot框架,以下是pom文件

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.kinglong.http</groupId>
<artifactId>http-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>http-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>RELEASE</version>
</dependency>


<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>RELEASE</version>
</dependency>


<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
 

說明:服務端的簽名生成類,簽名生成的輔助類都與客戶端相同,所以,這里就不寫了.

 

服務端的公共常量類:

 

package com.kinglong.http.constans;

public class Constans {
//服務端私鑰
public static final String SERVER_PRIVATE_KEY="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqUbW/zXiTJAgSktCfLGj/hFSS7w8T5ywbMfTvdehbzE91cAfwLtjeKzIMpIKVMQZ5L9JqqKhg8Uj5iUPQRqDUMShU+QNwl5aw+/gVmvZk7bAJ6fCpSsfkEztlqlIEt6eSB2FMXdIhQkytN8k9pEkzXBvvlI9rAc7XO54zRy9xt4Y0k7IIbr1c+Gi6vQJ6L8dx+VjdtdXH2Qofy+8CZuiwA6sjDB0ZKdXVipKUCkGaihsZhxoU+/kKWe2CGr7o/W63ZFI6sqKevRDjBziQimdJj6PahDbQ5nutle4vIaYxKyCW8bCBMijEeqRelY/eoYWZgdJCAYIwVbGVPW3d7UBrAgMBAAECggEBAJtfkxf4T4iblCmteVfb4aVHiQfJwc18VEYy2qkgvOoRhmMx4mv/sKNscGoMIXwMj0U6lQ/r8D8PnmzWBeEYrVslxQ9PYw3xm+y0z+qVxTTpiHBi08L8j0HHMaZbLBtVly6mQOKzrB/fJafXfmQXXRfXbTywH+2UZqb+oiFRTTzEnFMyku5HquA27Mp+K4KNFTVaKiCSadwz+XyFOf1cmUn4oRlYnhgbMKgn2JSyQLfJ5SSgYhnxat1Qbg1HDhOzo6L/NQwCkTzo3B52X56EQXZkk2p1pVRZiwaP/3FCHEenOv9jy+ZffdUFECRCv0Aw2isWqZ4zuVrgWCI801W/AVECgYEA0gwkMSHWlJaGtWhYifgWcOSr/v9l/SAiD0VN57j4THWIAq30WtOVE/ta4XhbSasxcHJIqNckq2inEm2b3jwcS7YKzlfKGl9xw8uL3yVM1YXtKwGrJr1xk8pcWOVBmFaAd8BaOHwsMkE8EhgiwZSctTbmRNeiOz6lIOj2aDu6fE0CgYEAz5SPKeNay00LIGk4Os+Zev1JPhwW4tfwTJxXV97TjvgYoRwUwV6XNjiXoxQD5iOXieK7Cy0GDMzJWxXpmEpI5BVv2X6GMkkH5iXz4rGtzsMNWZ5lF3d2PpRLvRIrSi3btevTohN7UkogyjEPhuEnV47Emsev36lB+bcJwgLBK5cCgYAuEaemdwt/T3yAMUCqEhWp8R2gMhgGapPN0Z+CoVkkO+r223xqp1ldJpYKOcGb6MZRKV+yWG2cgrmSGyRCm+CA4o6AL1UOb7yd+vjUmnO9qUAZXKZTOt28Unfqr22xoddPbIrdNK7k3tX0CgMlfhjYzg+3LaxRXi4Nh8rzlZYTSQKBgBahgq41bEun3aOt9QRsZ7ZB8P9FfrVCh59CmD8rOvNmVwERl62xS1kM+HM+FmK71KSixHOmd/djSDyW+f2xc5ryP1x979GBpsvPrXQ0nNdi6oyvuSPC0XBnKI63cWLH9yExUcRkzVgeXs7MZH33BBwGo6agSKtgv6Gi8/xj4n2HAoGBALD55ZzDDrB17ZOQSuEnn45R3FA62BGipCM8HFdZySeEOf9IPoOxy2xmzQtMc2VESRQeviw92E5gxNxrRgIHSQvPJryaHOjoMKI9+kDBejZvBEHWi23E4tobPUOUH/jBJJQ7Ue5aRB6IS/jlMzmVhan8vefnEXRDT5EnSfxV3Pa6";
//客戶端公鑰
public static final String CLIENT_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApRAKdDQx7W/cOYiX8jToeg3gkb9EYxhQNQiEOmcaXyHOuYdN4VBrA43GbVkEGX+3d9IYlQnEC8pSWCYnR0Gz3A1lC0YTm4FMRSBUN+1FvHPAxw4hzzVUYx+O3j4vbGSGG1bmljiZEvTQKEg00QwNb0u3sFqLAPVslEhLlkhJunkEFdNp5YX/3CLLt24g5qvGPX34PZS4BGGx+FULum0V4KqcvdDsRYdyYdep2wGV6gCTCgmciWKOQfWTeDaPEPxDGXvfAzRcmzqHe1k7kJq0uRTNLwrI0KXMwcJ/nZ8uYK1peGIFGIAFjp7z69ojF1Vy6m0I3FRDBSsK/+9SvclXoQIDAQAB";

}
 

服務端接口:

 

 

package com.kinglong.http.controller;

import com.kinglong.http.constans.Constans;
import com.kinglong.http.rto.ResponseObject;
import com.kinglong.http.utils.MyRSAUtils;
import com.kinglong.http.utils.SignUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Controller
public class HttpController {

@RequestMapping("/api/verifydemo")
@ResponseBody
public Object demo(@RequestParam(value = "realName")String realName,
@RequestParam(value = "phone")String phone,
@RequestParam(value = "idCard")String idCard,
@RequestParam(value = "bankCard")String bankCard,
@RequestParam(value = "sign")String sign){

ResponseObject responseObject = new ResponseObject();
Map<String,Object> map = new ConcurrentHashMap<String,Object>();

//簽名參數驗證
if (StringUtils.isEmpty(realName)){

responseObject.setErroMessage("真實姓名不能為空");
responseObject.setCode("0000");

}else if (StringUtils.isEmpty(phone)){

responseObject.setErroMessage("手機號不能為空");
responseObject.setCode("0000");

}else if (StringUtils.isEmpty(idCard)){

responseObject.setErroMessage("身份證不能為空");
responseObject.setCode("0000");

}else if (StringUtils.isEmpty(bankCard)){

responseObject.setErroMessage("銀行卡不能為空");
responseObject.setCode("0000");

}else if (StringUtils.isEmpty(sign)){

responseObject.setErroMessage("簽名不能為空");
responseObject.setCode("0000");

}else {
//封裝參數進行驗證
map.put("realName",realName);
map.put("phone",phone);
map.put("idCard",idCard);
map.put("bankCard",bankCard);

//注意看,這里使用的就是客戶端的公鑰進行簽名的解密
boolean isTrue = MyRSAUtils.signVerify(sign,SignUtils.generateSortSign(map),Constans.CLIENT_PUBLIC_KEY);

if (!isTrue){

responseObject.setErroMessage("簽名有誤,請核查后再次嘗試");
responseObject.setCode("0000");
}
}

//0000是失敗,1111是成功
String code = responseObject.getCode();
if(code!=null && code.equals("0000")){

map.clear();
map.put("result","0000");
map.put("resultDesc",responseObject.getErroMessage());

//使用服務端私鑰生成返回的簽名
String ret_sign = MyRSAUtils.sign(SignUtils.generateSortSign(map),Constans.SERVER_PRIVATE_KEY);
map.put("ret_sign",ret_sign);
responseObject.setObject(map);

}else {

map.clear();
map.put("result","ok");
map.put("resultDesc","驗證通過");

//使用服務端私鑰生成返回的簽名
String ret_sign = MyRSAUtils.sign(SignUtils.generateSortSign(map),Constans.SERVER_PRIVATE_KEY);
map.put("ret_sign",ret_sign);
responseObject.setObject(map);
responseObject.setCode("1111");
responseObject.setErroMessage("驗證成功");

}

return responseObject;
}

}
 

over,以上就是使用私鑰+公鑰進行http接口安全設計的簡單上手示例.

 

個人覺得,能把這個私鑰公鑰的搞明白了,其他的幾個接口安全設計也就不在話下了,基本看一下流程圖就能了然於心了.這里就放出參數安全設計的流程圖,具體代碼就不寫了,有興趣的可以嘗試着做一下.

為了方便練習的盆友快速理解,那就順帶提幾個需要注意的點吧.

 

在做:"接口參數簽名+時效性驗證(更加安全)"時,注意服務端返回的簽名就不需要再傳時間戳了,理應是要傳的,但是一般很少有人這么做.不過,要不要這么做,也得看公司的要求嘛.
在拼裝秘鑰的時候,注意字符串首先得進行排序,不管是升序還是降序,亦或是其他的順序(比如公司要求的順序),如果不先按照一個約定的順序進行排序,那么勢必會造成客戶端與服務端參數的字符串排列順序不同,致使無法驗證成功.這里就提供一個簡便的排序工具,並且使用MD5進行16進制加密
 

 

 

網址摘要地址:https://blog.csdn.net/hjl021/article/details/79286830

 

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM