目录
前言
当前时代,数据是王 道!当我们自己的平台有了足够大的数据量,就有可能诞生一个开放平台供第三方分析、使用。那么我们怎么去实现对外部调用接口的控制与鉴权呢?这是我们今天的重点——接口签名认证!!!
签名认证
开放平台会为每一位注册用户分配一个对应账户API KEY
和秘钥API SECRET
。我们为了保证接口的安全性,用户在每次接口的调用都需要上传一个所谓的签名(基于API KEY
和API SECRET
获取)
签名认证步骤:
- 首先生成一个Unix时间戳
timestamp
,时间精确到毫秒【即1970年1月1日(UTC/GMT的午夜)开始所经过的毫秒数】; - 生成随机数
nonce
(注:目前定义的是32位的,可以通过随机数工具类生成) ; - 1)将timestamp、nonce、API_KEY 这三个字符串依据“字符串首位字符的ASCII码”进行升序排列(排序过程中若出现ASCII码值相同的情况,则依次递增对下一位进行比较)(这种排序,”也就是俗称的字典序“),并将排序后的结果拼接成为一个字符串join_str;
2)接下来在用API_SECRET对上面生成这个字符串join_str做hmac-sha256 签名,并且以16进制编码,得到signature
; - 将上述得到的
timestamp
,nonce
,signature
,与API_KEY
按照#{k}=#{v}
并以 ',' 为区分拼接在一起形成新的字符串,这就是要返回签名认证字符串authorization
; - 当完成以上4步,我们就可以获取最终的签名认证字符串了。调用接口时,将通过签名认证步骤得到的值authorization 传给HTTP HEADER的Authorization对应的值;请求体部分按照相对应文档请求参数说明正确填写,正常发送请求,即可完成一次接口调用。
下面以java代码举例:
假设 API_KEY = "233453f1d1eb4eb5a5ad9c8dac0d02cc";
API_SECRETS = "444af44cd3a247c594438fb60d7b1d52";
1. 获得timestamp(unix时间戳),返回timestamp :"1633431976787"
实现方式:
String timestamp = Long.toString(System.currentTimeMillis());
2. 获得随机nonce,返回nonce: "3isQFsTjsnNOLvIPXhf3HlW17WSfQqp9"
可用下面方式实现:
String nonce = RandomStringUtils.randomAlphanumeric(32);
3.将timestamp、nonce、API_KEY 这三个字符串依据“字符串首位字符的ASCII码”进行升序排列(排序过程中若出现ASCII码值相同的情况,则依次递增对下一位进行比较),并join成一个字符串,返回join_str:"1633431976787233453f1d1eb4eb5a5ad9c8dac0d02cc3isQFsTjsnNOLvIPXhf3HlW17WSfQqp9"
可用下面方式实现:
public static String genOriString(String timestamp,String nonce,String API_KEY){
ArrayList<String> beforesort = new ArrayList<String>();
beforesort.add(API_KEY);
beforesort.add(timestamp);
beforesort.add(nonce);
Collections.sort(beforesort, new SpellComparator());
StringBuffer aftersort = new StringBuffer();
for (int i = 0; i < beforesort.size(); i++) {
aftersort.append(beforesort.get(i));
}
String join_str = aftersort.toString();
return join_str;
}
4. 用API_SECRET对join_str做hmac-sha256签名,且以16进制编码,返回signature:"7d37d14406323f0ab30d1d4db1e7f2eb27abac42f3de00d619025108fa6cd5e4"
可用下面方式实现:
public static String genEncryptString(String join_str, String API_SECRET){
Key sk = new SecretKeySpec(API_SECRET.getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk);
final byte[] hmac = mac.doFinal(join_str.getBytes());//完成hmac-sha256签名
StringBuilder sb = new StringBuilder(hmac.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : hmac) {
formatter.format("%02x", b);
}
String signature = sb.toString();//完成16进制编码
return signature;
}
5. 将上述的值按照 #{k}=#{v}
并以 ',' join在一起,返回签名认证字符串:
"key=2371d3f1d1eb4eb5a5ad9c8dac0d02cc,timestamp=1638431976741,nonce=3isWSsTjsnNOLvIPXhf3HlW17WSfQqp9,signature=7d37d14406323f0ab30d1d4db1e7f2eb27abac42f3de00d619025108fa6cd5e4"
可用下面方式实现:
public static String genauthorization(String API_KEY, String timestamp, String nonce, String signature){
String authorization = "key=" + API_KEY
+",timestamp=" + timestamp
+",nonce=" + nonce
+",signature=" + signature;
return authorization;
}
6. 将该签名认证字符串赋值给HTTP HEADER 的 Authorization
中,完成一次接口访问。
DEMO
pom文件中添加一下支持
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
具体实现代码:
import java.util.Comparator;
public class SpellComparator implements Comparator<Object> {
public int compare(Object o1, Object o2) {
try{
String s1 = new String(o1.toString().getBytes("GB2312"), "ISO-8859-1");
String s2 = new String(o2.toString().getBytes("GB2312"), "ISO-8859-1");
return s1.compareTo(s2);
}catch (Exception e){
e.printStackTrace();
}
return 0;
}
}
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.client.ClientProtocolException;
public class GenerateString {
// 开放平台注册获取id(API KEY)
public static final String id = "22bfe9745135";
// 开放平台注册获取密钥(API SECRET)
public static final String secret = "19fbdc10";
private static final String HASH_ALGORITHM = "HmacSHA256";
static String timestamp = Long.toString(System.currentTimeMillis());
static String nonce = RandomStringUtils.randomAlphanumeric(32);
public static String genOriString(String api_key){
ArrayList<String> beforesort = new ArrayList<String>();
beforesort.add(api_key);
beforesort.add(timestamp);
beforesort.add(nonce);
Collections.sort(beforesort, new SpellComparator());
StringBuffer aftersort = new StringBuffer();
for (int i = 0; i < beforesort.size(); i++) {
aftersort.append(beforesort.get(i));
}
String OriString = aftersort.toString();
return OriString;
}
public static String genEncryptString(String genOriString, String api_secret)throws SignatureException {
try{
Key sk = new SecretKeySpec(api_secret.getBytes(), HASH_ALGORITHM);
Mac mac = Mac.getInstance(sk.getAlgorithm());
mac.init(sk);
final byte[] hmac = mac.doFinal(genOriString.getBytes());
StringBuilder sb = new StringBuilder(hmac.length * 2);
@SuppressWarnings("resource")
Formatter formatter = new Formatter(sb);
for (byte b : hmac) {
formatter.format("%02x", b);
}
String EncryptedString = sb.toString();
return EncryptedString;
}catch (NoSuchAlgorithmException e1){
throw new SignatureException("error building signature, no such algorithm in device "+ HASH_ALGORITHM);
}catch (InvalidKeyException e){
throw new SignatureException("error building signature, invalid key " + HASH_ALGORITHM);
}
}
public static String genHeaderParam(String api_key, String api_secret) throws SignatureException{
String GenOriString = genOriString(api_key);
String EncryptedString = genEncryptString(GenOriString, api_secret);
String HeaderParam = "key=" + api_key
+",timestamp=" + timestamp
+",nonce=" + nonce
+",signature=" + EncryptedString;
System.out.println(HeaderParam);
return HeaderParam;
}
public static void main(String[] args) throws ClientProtocolException, IOException, SignatureException{
String s = genHeaderParam(id, secret);
System.out.println(s);
}
}
好了,今天关于开放平台之接口认证鉴权就到这里。
欢迎大家点击下方卡片关注《coder练习生》