开放api接口平台:appid、appkey、appsecret

22 篇文章 7 订阅

文章目录
一、什么是appid、appkey、appsecret
二、云服务AppId或AppKey和AppSecret生成策略
三、API接口开发安全性
四、基于AccessToken方式实现API设计
五、常见问题总结
做API接口,为什么access_token要放在Header头里传递?
六、参考
一、什么是appid、appkey、appsecret
AppID:应用的唯一标识AppKey:公匙(相当于账号)AppSecret:私匙(相当于密码)

token:令牌(过期失效)

app_id 是用来标记你的开发者账号的, 是你的用户id, 这个id 在数据库添加检索, 方便快速查找。

app_key 和 app_secret 是一对出现的账号, 同一个 app_id 可以对应多个 app_key+app_secret, 这样 平台就可以分配你不一样的权限, 比如 app_key1 + app_secect1 只有只读权限 但是 app_key2+app_secret2 有读写权限… 这样你就可以把对应的权限 放给不同的开发者. 其中权限的配置都是直接跟app_key 做关联的, app_key 也需要添加数据库检索, 方便快速查找。

至于为什么 要有app_key + app_secret 这种成对出现的机制呢, 因为 要加密, 通常 在首次验证(类似登录场景) , 你需要用 app_key(标记要申请的权限有哪些) + app_secret(密码, 表示你真的拥有这个权限) 来申请一个token, 就是我们经常用到的 access_token, 之后的数据请求, 就直接提供access_token 就可以验证权限了.

简化的场景:

省去 app_id, 他默认每一个用户有且仅有一套权限配置, 所以直接将 app_id = app_key , 然后外加一个app_secret就够了.
省去app_id 和 app_key, 相当于 app_id = app_key = app_secret, 通常用于开放性接口的地方, 特别是很多地图类api 都采用这种模式, 这种模式下, 带上app_id 的目的仅仅是统计 某一个用户调用接口的次数而已了.
使用方法

向第三方服务器请求授权时,带上AppKey和AppSecret(需存在服务器端)

第三方服务器验证AppKey和AppSecret在DB中有无记录

如果有,生成一串唯一的字符串(token令牌),返回给服务器,服务器再返回给客户端

客户端下次请求敏感数据时带上令牌

二、云服务AppId或AppKey和AppSecret生成策略
[推荐]云服务AppId或AppKey和AppSecret生成策略
参考URL: https://www.cnblogs.com/owenma/p/11419341.html

Java 原生的UUID为36位 or 32位,太长。参考原博文,分析算法:

关于appid生成:

首先,它先获取,32个(去掉了-)十六进制字符串。

    String uuid = UUID.randomUUID().toString().replace("-", "");
1
将其分成8组,每4个字符为一组str,如下16进制字符串转10进制int型

        int x = Integer.parseInt(str, 16);
1
然后通过模62操作,结果作为索引取出字符,

        chars[x % 0x3E]
1
这里x % 0x3E 不好理解,其实Integer.parseInt(“3E”, 16); 结果是62,所以这里x % 0x3E就是x模62(x % 62),根据模的结果在你的定义的62个可见字符数组中取对应索引的字符。

这样总共8组,一组取一个字符,8组取8个字符,就是你要的appid。

个人对该算法思考:它其实就是利用uuid的字符串,分成8组,做随机数模62,感觉uuid的作用就是随机数的作用。那么问题就是,uuid分成的8组每组真正都随机么?假如随机,那么我们为什么不直接生成随机数,生成8组,为什么要用uuid呢?还是说都是造随机两个没有什么本质区别,都可以,只是作者使用了uuid来造而已?
如果有算法爱好者,希望可以解答!

关于appsecrect,文章中是appid+固定字符串做sha1,感觉这样有安全风险,别人知道appid知道算法,就可以计算出你的appsecrect。如下,个人改成了 sha1(appid + uuid)生成secrect。

/**
 * 随机产生唯一的app_key和app_secret
 */
public class AppUtils {

    private final static String[] chars = new String[]{"a", "b", "c", "d", "e", "f",
            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z"};

    /**
     * @Description: <p>
     * 短8位UUID思想其实借鉴微博短域名的生成方式,但是其重复概率过高,而且每次生成4个,需要随即选取一个。
     * 本算法利用62个可打印字符,通过随机生成32位UUID,由于UUID都为十六进制,
     * 所以将UUID分成8组,每4个为一组,然后通过模62操作,结果作为索引取出字符,
     * 这样重复率大大降低。
     * 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。
     * </p>
     */
    public static String getAppId() {
        StringBuffer shortBuffer = new StringBuffer();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(chars[x % 0x3E]);
        }
        return shortBuffer.toString();

    }

    /**
     *  算法: sha1(appid+uuid) 生成AppSecret
     */
    public static String getAppSecret(String appId) {
        try {
            StringBuffer sb = new StringBuffer();
            String uuid = UUID.randomUUID().toString();

            sb.append(appId).append(uuid);

            String str = sb.toString();
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        }  catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return appId;
    }

    public static void main(String[] args) {
        String appId = getAppId();
        String appSecret = getAppSecret(appId);
        System.out.println("appId: "+appId);
        System.out.println("appSecret: "+appSecret);
    }
}


三、API接口开发安全性
[推荐]API接口安全性设计
参考URL: https://www.jianshu.com/p/c6518a8f4040

接口的安全性主要围绕token、timestamp和sign三个机制展开设计,保证接口的数据不会被篡改和重复调用,下面具体来看:

Token授权机制
用户使用用户名密码登录后服务器给客户端返回一个Token(通常是UUID),并将Token-UserId以键值对的形式存放在缓存服务器中。服务端接收到请求后进行Token验证,如果Token不存在,说明请求无效。Token是客户端访问服务端的凭证。

时间戳超时机制
用户每次请求都带上当前时间的时间戳timestamp,服务端接收到timestamp后跟当前时间进行比对,如果时间差大于一定时间(比如5分钟),则认为该请求失效。时间戳超时机制是防御DOS攻击的有效手段。

签名机制
将 Token 和 时间戳 加上其他请求参数再用MD5或SHA-1算法(可根据情况加点盐)加密,加密后的数据就是本次请求的签名sign,服务端接收到请求后以同样的算法得到签名,并跟当前的签名进行比对,如果不一样,说明参数被更改过,直接返回错误标识。签名机制保证了数据不会被篡改。

拒绝重复调用(非必须)
客户端第一次访问时,将签名sign存放到缓存服务器中,超时时间设定为跟时间戳的超时时间一致,二者时间一致可以保证无论在timestamp限定时间内还是外 URL都只能访问一次。如果有人使用同一个URL再次访问,如果发现缓存服务器中已经存在了本次签名,则拒绝服务。如果在缓存中的签名失效的情况下,有人使用同一个URL再次访问,则会被时间戳超时机制拦截。这就是为什么要求时间戳的超时时间要设定为跟时间戳的超时时间一致。拒绝重复调用机制确保URL被别人截获了也无法使用(如抓取数据)。

在以上三中机制的保护下,

如果有人劫持了请求,并对请求中的参数进行了修改,签名就无法通过;

如果有人使用已经劫持的URL进行DOS攻击,服务器则会因为缓存服务器中已经存在签名或时间戳超时而拒绝服务,所以DOS攻击也是不可能的;

所有的安全措施都用上的话有时候难免太过复杂,在实际项目中需要根据自身情况作出裁剪,比如可以只使用签名机制就可以保证信息不会被篡改,或者定向提供服务的时候只用Token机制就可以了。如何裁剪,全看项目实际情况和对接口安全性的要求~

四、基于AccessToken方式实现API设计
基于AccessToken方式实现API设计
参考URL: https://www.cnblogs.com/kevin-ying/p/10800934.html
Spring Boot入门教程(四十三): API接口设计之token、timestamp、sign
参考URL: https://blog.csdn.net/vbirdbest/article/details/80789817

需求:

A、B机构需要调用X服务器的接口,那么X服务器就需要提供开放的外网访问接口。

分析:

1、开放平台提供者X,为每一个合作机构提供对应的appid、app_secret。

2、appid是唯一的(不能改变),表示对应的第三方合作机构,用来区分不同机构的。

3、app_secret在传输中实现加密功能(秘钥),该秘钥可以发生改变的。

4、为什么app_secret是可以改变的?调用接口需要appid+app_secret生成对应的access_token(临时性),如果appid和app_secret被泄密,产生安全性问题,如果一但发现被泄密,可以重新生成一个app_secret。

原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。

二、开发步骤

1、使用appid+app_secret生成对应的access_token

1.获取生成的AppId和appSecret,并验证是否可用
2.删除之前的accessToken
3.AppId和appSecret保证生成对应唯一的accessToken
注意:以上第二步必须保证在同一事务中
4.返回最新的accessToken

2、使用accessToken调用第三方接口

1.获取对应的accessToken
2.使用AccessToken查询redis对应的value(appId)
3.如果没有获取到对应的appid,直接返回错误提示

4.如果能获取到对应的appid,使用appid查询对应的APP信息
5.使用appId查询数据库app信息,获取is_flag状态,如果为1,则不能调用接口,否则正常执行
6.直接调用接口业务

五、常见问题总结
做API接口,为什么access_token要放在Header头里传递?
如果是OAuth2, 使用 Header传递token是属于规范的一种,Header中有一个Authorization头专门用于存放认证信息
每一次登录,会生成一个新的Token, 此时旧的token并不会立即失效(取决于该token生成时,设置的失效时间)

六、参考
API接口开发安全性,你是如何解决的
参考URL: https://www.sohu.com/a/281386848_652662
[推荐]API接口安全性设计
参考URL: https://www.jianshu.com/p/c6518a8f4040
WebApi安全性 使用TOKEN+签名验证
参考URL: https://www.cnblogs.com/MR-YY/p/5972380.html
云服务AppId或AppKey和AppSecret生成策略
参考URL: https://www.cnblogs.com/owenma/p/11419341.html
认识和使用JWT
参考URL: https://blog.csdn.net/qq_40493277/article/details/99626681
 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以帮您编写一个使用SM国密生成appkeyappsecret的Java代码。以下是示例代码: ``` import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; public class SMAppKeyGenerator { private static final String ALGORITHM = "SM3withSM2"; private static final String KEY_ALGORITHM = "EC"; private static final String PROVIDER = "GM"; public static void main(String[] args) throws Exception { String appId = "your_app_id"; String appSecret = generateAppSecret(); String appKey = generateAppKey(appId, appSecret); System.out.println("AppId: " + appId); System.out.println("AppSecret: " + appSecret); System.out.println("AppKey: " + appKey); } private static String generateAppSecret() throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM, PROVIDER); keyPairGenerator.initialize(new ECGenParameterSpec("sm2p256v1")); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); byte[] encodedPrivateKey = privateKey.getEncoded(); return bytesToHexString(encodedPrivateKey); } private static String generateAppKey(String appId, String appSecret) throws Exception { byte[] appIdBytes = appId.getBytes("UTF-8"); byte[] appSecretBytes = hexStringToBytes(appSecret); byte[] data = new byte[appIdBytes.length + appSecretBytes.length]; System.arraycopy(appIdBytes, 0, data, 0, appIdBytes.length); System.arraycopy(appSecretBytes, 0, data, appIdBytes.length, appSecretBytes.length); MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM, PROVIDER); byte[] digest = messageDigest.digest(data); return bytesToHexString(digest); } private static String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } private static byte[] hexStringToBytes(String hexString) { int len = hexString.length() / 2; byte[] bytes = new byte[len]; for (int i = 0; i < len; i++) { bytes[i] = (byte) Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16); } return bytes; } } ``` 这段代码使用了SM2算法生成了一个公私钥对,然后使用SM3withSM2算法对AppIdAppSecret进行哈希计算,得到AppKey。请注意,这里使用了Bouncy Castle作为Java加密库的提供者,因为它支持SM算法。如果您的Java环境中没有安装Bouncy Castle,您需要先下载并安装它。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值