前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >三方接口调用设计方案

三方接口调用设计方案

作者头像
用户1142828
发布2024-12-08 23:33:08
发布2024-12-08 23:33:08
1090
举报

API接口设计

根据具体需求和业务场景,以下是一个简单示例的 API 接口设计:

一、获取资源列表接口

  • URL:/api/resources
  • HTTP 方法:GET 或者 POST
  • 请求参数
    • page(可选):页码
    • limit(可选):每页限制数量
  • 响应
    • 成功状态码:200 OK
    • 响应体:返回资源列表的 JSON 数组

二、创建资源接口

  • URL:/api/resources
  • HTTP 方法:POST
  • 请求参数
    • name(必填):资源名称
    • description(可选):资源描述
  • 响应
    • 成功状态码:201 Created
    • 响应体:返回新创建资源的 ID 等信息

三、更新资源接口

  • URL:/api/resources/{resourceId}
  • HTTP 方法:PUT
  • 请求参数
    • resourceId(路径参数,必填):资源 ID
    • name(可选):更新后的资源名称
    • description(可选):更新后的资源描述
  • 响应
    • 成功状态码:200 OK

四、删除资源接口

  • URL:/api/resources/{resourceId}
  • HTTP 方法:DELETE
  • 请求参数
    • resourceId(路径参数,必填):资源 ID
  • 响应
    • 成功状态码:204 No Content

三方接口安全性考虑及相关实现示例

一、总体安全措施概述

为保障三方接口的安全性,可采取多方面措施,包括使用 HTTPS 协议确保数据传输安全,利用 AK 和签名进行身份验证以及对请求验签来防止非法请求与重放攻击,还有对敏感数据进行加密传输等。不过具体实现细节会因项目需求而存在差异,并且在实际开发中,还需兼顾错误处理、异常情况处理以及日志记录等方面内容。

二、防止重放攻击措施及实现

(一)使用 Nonce 和 Timestamp

  1. 生成 Nonce 和 Timestamp
    • Nonce 应是随机且唯一的字符串,可借助 UUID 或其他随机字符串生成算法来创建。例如,使用 UUID.randomUUID().toString() 就能生成符合要求的随机字符串作为 Nonce。
    • Timestamp 通常采用标准的 Unix 时间戳格式(以秒为单位),可通过获取当前系统时间并转换为相应格式来生成,像 System.currentTimeMillis() / 1000 就能获取到当前的 Unix 时间戳(以秒计)。
  2. 在每个请求中包含 Nonce 和 Timestamp 请求中需将生成的 Nonce 和 Timestamp 作为参数添加进去,可以通过 URL 参数、请求头或请求体的方式传递,且要确保它们在每个请求中都是唯一且正确的。比如在一个基于 HTTP 的接口请求中,可将其添加到请求头里:
代码语言:http
复制
GET /api/resource HTTP/1.1
Host: example.com
Nonce: [具体生成的Nonce值]
Timestamp: [具体生成的时间戳值]
...

3.服务器端验证 Nonce 和 Timestamp

  • 服务器接收到请求后,要先验证 Nonce 和 Timestamp 的有效性。检查 Nonce 是否已被使用过,若已使用则可能是重放攻击,需拒绝该请求;同时查看 Timestamp 是否处于合理的时间范围内,超出预定有效期的请求应视作无效。
  • 例如,在 Java 代码中可通过如下逻辑验证(伪代码示意):
代码语言:java
复制
// 假设从请求中获取到了nonce和timestamp
String nonce = request.getHeader("Nonce");
long timestamp = Long.parseLong(request.getHeader("Timestamp"));
if (isNonceUsed(nonce) || isTimestampExpired(timestamp)) {
    throw new IllegalRequestException("可能是重放攻击,请求无效");
}

4 存储和管理 Nonce

  • 服务器为验证 Nonce 是否已被用过,需存储已使用的 Nonce,可利用数据库、缓存(如 Redis)等持久化存储方式管理其状态。
  • 同时要定期清理过期的 Nonce,防止存储资源被过度占用。比如在 Redis 中,可以设置过期时间来自动清理过期的 Nonce 记录:
代码语言:java
复制
// 使用Redis存储Nonce,并设置过期时间为60秒
redisTemplate.opsForValue().set(nonce, nonce, 60, TimeUnit.SECONDS);

5.设置有效期

根据实际业务场景和需求,选择合适的有效期,比如几分钟或几小时。例如对于一些对实时性要求较高的支付接口,有效期可设置为较短的 5 分钟左右,即超过 300 秒的请求视为过期无效。

(二)添加过期时间

在请求中添加一个过期时间字段(如 token 的有效期),服务端验证请求的时间戳是否在有效期内,超过过期时间的请求应予以拒绝。例如在请求中携带类似 expire_time 的字段,在服务器端进行如下验证(伪代码示意):

代码语言:java
复制
long expireTime = Long.parseLong(request.getParameter("expire_time"));
long currentTime = System.currentTimeMillis() / 1000;
if (currentTime > expireTime) {
    throw new IllegalRequestException("请求已过期,无效");
}

(三)防篡改、防重放攻击拦截器示例(Java 代码实现)

以下是 SignAuthInterceptor 类的代码解读,它实现了拦截 HTTP 请求并进行防重放攻击等验证的功能:

代码语言:java
复制
public class SignAuthInterceptor implements HandlerInterceptor {

    private RedisTemplate<String, String> redisTemplate;
    private String key;

    public SignAuthInterceptor(RedisTemplate<String, String> redisTemplate, String key) {
        this.redisTemplate = redisTemplate;
        this.key = key;
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        // 获取时间戳
        String timestamp = request.getHeader("timestamp");
        // 获取随机字符串
        String nonceStr = request.getHeader("nonceStr");
        // 获取签名
        String signature = request.getHeader("signature");

        // 判断时间是否大于xx秒(防止重放攻击),与当前时间比不能大于xx秒
        long NONCE_STR_TIMEOUT_SECONDS = 60L;
        if (StrUtil.isEmpty(timestamp) || DateUtil.between(DateUtil.date(Long.parseLong(timestamp) * 1000), DateUtil.date(), DateUnit.SECOND) > NONCE_STR_TIMEOUT_SECONDS) {
            throw new BusinessException("invalid  timestamp");
        }

        // 判断该用户的nonceStr参数是否已经在redis中(防止短时间内的重放攻击)
        Boolean haveNonceStr = redisTemplate.hasKey(nonceStr);
        if (StrUtil.isEmpty(nonceStr) || Objects.isNull(haveNonceStr) || haveNonceStr) {
            throw new BusinessException("invalid nonceStr");
        }

        // 对请求头参数进行签名
        if (StrUtil.isEmpty(signature) ||!Objects.equals(signature, this.signature(timestamp, nonceStr, request))) {
            throw new BusinessException("invalid signature");
        }

        // 将本次用户请求的nonceStr参数存到redis中设置xx秒后自动删除
        redisTemplate.opsForValue().set(nonceStr, nonceStr, NONCE_STR_TIMEOUT_SECONDS, TimeUnit.SECONDS);

        return true;
    }

    private String signature(String timestamp, String nonceStr, HttpServletRequest request) throws UnsupportedEncodingException {
        Map<String, Object> params = new HashMap<>(16);
        Enumeration<String> enumeration = request.getParameterNames();
        if (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getParameter(name);
            params.put(name, URLEncoder.encode(value, CommonConstants.UTF_8));
        }
        String qs = String.format("%s×tamp=%s&nonceStr=%s&key=%s", this.sortQueryParamString(params), timestamp, nonceStr, key);
        log.info("qs:{}", qs);
        String sign = SecureUtil.md5(qs).toLowerCase();
        log.info("sign:{}", sign);
        return sign;
    }

    /**
     * 按照字母顺序进行升序排序
     *
     * @param params 请求参数 。注意请求参数中不能包含key
     * @return 排序后结果
     */
    private String sortQueryParamString(Map<String, Object> params) {
        List<String> listKeys = Lists.newArrayList(params.keySet());
        Collections.sort(listKeys);
        StrBuilder content = StrBuilder.create();
        for (String param : listKeys) {
            content.append(param).append("=").append(params.get(param).toString()).append("&");
        }
        if (content.length() > 0) {
            return content.subString(0, content.length() - 1);
        }
        return content.toString();
    }
}

这个拦截器主要做了以下几件事:

  1. 从请求头获取 timestampnonceStrsignature 参数,对时间戳进行时效性验证,防止请求时间过长(超过 60 秒)可能存在的重放攻击情况,不符合要求则抛出异常。
  2. 检查 nonceStr 是否已存在于 Redis 中,若存在则认为是非法请求(短时间内重放),同样抛出异常。
  3. 利用获取到的参数以及请求中的其他参数重新计算签名,并与请求中传来的签名对比,不一致则判定签名无效,拒绝请求。
  4. 若上述验证都通过,将本次请求的 nonceStr 参数存入 Redis 并设置 60 秒后自动删除,然后放行请求。

三、对敏感数据进行加密传输(使用 TLS 协议)

RPC通信一般是使用TLS加密

(一)基本步骤

  1. 在服务器上配置 TLS 证书(包括公钥和私钥):服务器需要获取有效的 TLS 证书,并妥善配置好相关公私钥信息,证书通常以特定格式(如.jks)存储,且配置时需要提供正确的密码用于加载证书等操作。
  2. 客户端和服务器之间建立 TLS 连接,客户端向服务器发送 HTTPS 请求:客户端发起请求时,会按照 HTTPS 协议要求与服务器进行交互,尝试建立 TLS 加密连接。
  3. 在 TLS 握手期间,客户端和服务器协商加密算法和密钥交换方法:双方会根据各自支持的加密算法和密钥交换机制进行协商,选择一种双方都认可的安全方式来保障后续数据传输安全。
  4. 握手成功后,客户端和服务器之间的所有数据传输都会经过加密处理:这样一来,传输过程中的敏感数据都能得到有效保护,防止被窃取或篡改。

(二)Java 示例代码解读

以下是一段使用 Java 实现基于 TLS 进行加密传输的示例代码:

代码语言:java
复制
// 创建SSLContext对象
SSLContext sslContext = SSLContext.getInstance("TLS");

// 初始化SSLContext,加载证书和私钥
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream("keystore.jks"), "password".toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "password".toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);

sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());

// 创建HttpsURLConnection连接
URL url = new URL("https://api.example.com/endpoint");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());

// 设置其他请求参数、发送请求、处理响应等

这段代码的主要步骤及作用如下:

  1. 首先通过 SSLContext.getInstance("TLS") 创建了一个 SSLContext 对象,指定使用 TLS 协议。
  2. 接着加载服务器的证书和私钥信息到 KeyStore 中,需要提供正确的文件路径(如 "keystore.jks")以及对应的密码( "password"),并利用 KeyManagerFactoryTrustManagerFactory 进行初始化配置,这一步是为后续 SSLContext 的初始化做准备。
  3. 然后使用配置好的密钥管理相关对象初始化 SSLContext,使其具备加密通信所需的环境基础。
  4. 创建 HttpsURLConnection 对象与指定的 URL(如 "https://api.example.com/endpoint")建立连接,并通过 connection.setSSLSocketFactory(sslContext.getSocketFactory()) 设置 TLS 的安全套接字工厂,使得后续通过该连接进行的数据传输都能基于 TLS 协议进行加密处理。之后还可以按常规流程进行请求参数设置、发送请求以及处理响应等操作。

请注意,实际部署时,要将示例代码中的证书和私钥文件路径替换为真实有效的文件路径,并提供正确的密码,同时可能还需根据具体项目要求做更多的配置优化工作,确保三方接口的加密传输安全可靠。

AK 和 SK 生成及管理方案

一、API 密钥管理系统构建

需开发 API 密钥管理系统,可独立部署或集成于主应用。其核心功能为高效生成、精准管理 AK 和 SK,成为保障接口安全的核心枢纽。

二、AK 和 SK 生成策略
  • AK 生成:作为公开客户身份标识,可运用随机字符串生成算法或 UUID 工具生成,确保全球唯一性,便于精准识别客户。例如在 Java 中利用 UUID.randomUUID().toString() 即可快速生成 AK。
  • SK 生成:作为机密私钥,关乎系统安全命脉,可采用强哈希函数(如 Argon2、PBKDF2 等)处理随机字符串,增强保密性与抗破解力,确保身份验证签名与访问令牌加密安全可靠。
三、存储与权限管理
  • 数据存储:将 AK、SK 与客户多元信息存入数据库(如 MySQL、PostgreSQL 等),建立强关联模型,优化数据检索与管理效率。
  • 权限管控:精细设计权限系统,严格限制访问权限,采用加密技术(如 AES、RSA 等)加密 SK,构建多层防护屏障,确保仅授权人员可触碰 AK 和 SK。
四、分发机制设计
  • 多渠道分发:为客户打造界面交互、API 调用、自助注册等多元 AK 和 SK 获取途径,贴合不同客户技术能力与使用习惯。
  • 安全传输保障:分发全程依托 TLS/SSL 加密通道或 VPN 专网,运用数字证书认证、密钥交换协议等技术,确保密钥传输零风险、零泄漏。
五、安全强化与最佳实践
  • 审计与优化:周期性开展 API 密钥管理系统安全审计,深度扫描漏洞,依据业界最佳实践持续优化升级。
  • 动态轮换策略:定期(如按季或半年)更新 AK 和 SK,旧密钥退役与新密钥激活无缝衔接,最大程度降低潜在安全威胁,提升系统长期安全性与稳定性。

API接口设计注意事项

一、幂等性问题解决思路

  • 幂等性概念:指任意多次请求的执行结果和一次请求的执行结果所产生的影响相同,像查询操作天然是幂等的,而新增操作通常是非幂等的,因其每次执行都会改变数据库状态。
  • 解决方法:提供生成全局唯一随机数的接口,调用接口时带入该随机数。首次调用业务处理成功后,以随机数为键、操作结果为值存入 Redis,并设置过期时长;再次调用时,查询 Redis,若键存在则判定为重复提交,直接返回错误,以此确保相关操作的幂等性。

二、版本控制策略

对于成熟的 API 文档,发布后不宜随意修改接口,若需新增或修改接口,应加入版本控制。版本号可采用整数或浮点数类型,且一般在接口地址中体现,如 http://ip:port//v1/listhttp://ip:port//v2/list,方便区分不同版本的接口,保障接口更新迭代过程中的兼容性与可管理性。

三、生成签名 sign 的详细步骤

  1. 参数整理排序:先将所有参数(除 sign 本身以及值为空的参数外,如 appId、timeStamp、nonce 等)按 key 名升序排序存储。例如对于请求 http://www.xxx.com/openApi?sign=sign_value&k1=v1&k2=v2&method=cancel&k3=&kX=vX,去除 sign 参数和值为空的 k3 参数后,对剩余参数按升序排序得到 appId=zs001&k1=v1&k2=v2&kX=vX&method=cancel&nonce=1234567890&timeStamp=1612691221000
  2. 拼接字符串:把排序后的参数按 key1value1key2value2…keyXvalueX 的方式拼接成一个字符串,上述例子中拼接后为 appIdzs001k1v1k2v2kXvXmethodcancelnonce1234567890timeStamp1612691221000
  3. 添加密钥:将分配给调用方的密钥 secret 拼接在第二步得到的字符串最后面,假设密钥是 miyao,则新字符串变为 appIdzs001k1v1k2v2kXvXmethodcancelnonce1234567890timeStamp1612691221000miyao
  4. 计算签名:计算第三步字符串的 hmac-sha256。需注意,计算 hmac-sha256 之前调用方要确保签名加密字符串编码与提供方一致,如统一使用 utf-8 编码或者 GBK 编码,否则签名校验会失败。

四、Token 相关内容

以下是对上述“Token相关内容”的总结:

Token概念及分类

  • 概念:Token即访问令牌access token,是用于接口中标识接口调用者身份、凭证的工具,能减少用户名和密码的传输次数。
  • 分类
    • API Token(接口令牌):用于访问不需要用户登录的接口,例如登录、注册、基本数据获取等。获取它需要用appId、timestamp和sign来换取(sign=加密(参数1+…+参数n+timestamp+key))。
    • USER Token(用户令牌):用于访问需用户登录后的接口,比如获取用户基本信息、保存、修改、删除等操作,获取需凭借用户名和密码来换。

Token相关验证机制

  • Token身份验证:用户登录时向服务器提供认证信息(如账号和密码),服务器验证成功后返回Token给客户端,客户端在本地缓存。后续每次发起请求时携带该Token,服务端检查其有效性,若有效则放行请求,若无效(如Token错误或过期)则拒绝请求。不过这种方式存在Token被劫持、伪造请求以及篡改参数等弊端。
  • Token + 签名验证:和普通接口签名规则类似,会为客户端分配appSecret(密钥,用于接口加密,不参与传输),将appSecret和所有请求参数组合成一个字符串,依照签名算法生成签名值,发送请求时将其与请求一并发送给服务器验证。这样即便Token被劫持,由于对方不知道appSecret和签名算法,也就无法伪造请求和篡改参数,并且借助token还能获取正确的用户状态。例如在登陆、退出请求以及后续请求中,客户端可把appId改为token按相应规则操作就行。

五. 使用 POST 作为接口请求方式

在接口调用中,常用的请求方式有 GET 和 POST 两种。GET 请求会把参数直接暴露在浏览器 URL 里,并且对参数长度存在限制,从安全性角度考量相对薄弱。为增强接口的安全性,建议统一采用 POST 方式来发起请求,以此避免因参数暴露等问题带来的潜在安全风险。

六. 客户端 IP 白名单

  • 概念及作用:IP 白名单是一种安全策略,通过将接口的访问权限开放给特定的部分 IP,以此来阻止其他未授权的 IP 进行访问攻击,保障接口的访问安全。
  • 缺点及应对:不过它存在一定缺点,当客户端所在的 IP 发生迁移后,就需要重新联系服务提供者添加新的 IP 到白名单中,才能继续正常访问接口。
  • 设置方式及推荐:设置 IP 白名单的途径有多种,像传统的防火墙能实现该功能,另外 spring cloud alibaba 提供的组件 sentinel 也支持此项设置。但为了降低 API 的复杂度,更推荐运用防火墙规则来进行 IP 白名单的设置操作。

七. 单个接口针对 IP 限流

  • 限流目的:限流主要是为了更好地维护系统整体的稳定性,避免因过多的请求在短时间内涌入某个接口,导致系统出现性能问题甚至崩溃等情况。
  • 实现方式(基于 redis):借助 redis 来进行接口调用次数的统计,将“IP + 接口地址”作为 key,访问次数作为 value,每当有一次请求时,就让对应 value 的值加 1,同时设置合适的过期时长,通过这样的方式来限制接口在一定时间范围内的调用频率,合理控制接口的负载情况。

八. 记录接口请求日志

记录接口请求日志有着重要意义,当系统出现异常请求时,通过查看这些日志,能够快速定位到异常请求发生的位置,进而方便排查问题产生的原因。例如,可以采用 AOP(面向切面编程)的方式来对接口请求进行全局处理,高效地记录下每个请求的相关关键信息,为后续的问题追溯与分析提供有力依据。

九. 敏感数据脱敏

在接口调用环节,往往会涉及到像订单号这类敏感数据,出于数据安全和隐私保护的考量,通常需要对这类敏感数据进行脱敏处理。最常用的手段就是加密,其中安全性较高的 RSA 非对称加密方式应用较为广泛。非对称加密算法具备两个不同但又相互匹配的密钥,只有使用与之匹配的一对公钥和私钥,才能够完成对明文的加密和解密操作,以此确保敏感数据在传输和存储等过程中的安全性。实际上来说,使用https通信就没有问题了。

名词解释

名词

解释

APP_ID

每个业务垂类会申请一个app_id,例如给一家公司可能分配两个app_id。一个是查询旅游信息的,一个是查询信用卡账单信息的

参考文献
  1. 三方接口调用设计方案

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • API接口设计
    • 一、获取资源列表接口
    • 二、创建资源接口
    • 三、更新资源接口
    • 四、删除资源接口
  • 三方接口安全性考虑及相关实现示例
    • 一、总体安全措施概述
    • 二、防止重放攻击措施及实现
      • (一)使用 Nonce 和 Timestamp
      • (二)添加过期时间
      • (三)防篡改、防重放攻击拦截器示例(Java 代码实现)
    • 三、对敏感数据进行加密传输(使用 TLS 协议)
      • (一)基本步骤
      • (二)Java 示例代码解读
      • AK 和 SK 生成及管理方案
  • API接口设计注意事项
    • 一、幂等性问题解决思路
    • 二、版本控制策略
    • 三、生成签名 sign 的详细步骤
    • 四、Token 相关内容
    • Token概念及分类
    • Token相关验证机制
    • 五. 使用 POST 作为接口请求方式
    • 六. 客户端 IP 白名单
    • 七. 单个接口针对 IP 限流
    • 八. 记录接口请求日志
    • 九. 敏感数据脱敏
    • 名词解释
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档