根据具体需求和业务场景,以下是一个简单示例的 API 接口设计:
为保障三方接口的安全性,可采取多方面措施,包括使用 HTTPS 协议确保数据传输安全,利用 AK 和签名进行身份验证以及对请求验签来防止非法请求与重放攻击,还有对敏感数据进行加密传输等。不过具体实现细节会因项目需求而存在差异,并且在实际开发中,还需兼顾错误处理、异常情况处理以及日志记录等方面内容。
System.currentTimeMillis() / 1000
就能获取到当前的 Unix 时间戳(以秒计)。GET /api/resource HTTP/1.1
Host: example.com
Nonce: [具体生成的Nonce值]
Timestamp: [具体生成的时间戳值]
...
3.服务器端验证 Nonce 和 Timestamp
// 假设从请求中获取到了nonce和timestamp
String nonce = request.getHeader("Nonce");
long timestamp = Long.parseLong(request.getHeader("Timestamp"));
if (isNonceUsed(nonce) || isTimestampExpired(timestamp)) {
throw new IllegalRequestException("可能是重放攻击,请求无效");
}
4 存储和管理 Nonce
// 使用Redis存储Nonce,并设置过期时间为60秒
redisTemplate.opsForValue().set(nonce, nonce, 60, TimeUnit.SECONDS);
5.设置有效期
根据实际业务场景和需求,选择合适的有效期,比如几分钟或几小时。例如对于一些对实时性要求较高的支付接口,有效期可设置为较短的 5 分钟左右,即超过 300 秒的请求视为过期无效。
在请求中添加一个过期时间字段(如 token 的有效期),服务端验证请求的时间戳是否在有效期内,超过过期时间的请求应予以拒绝。例如在请求中携带类似 expire_time
的字段,在服务器端进行如下验证(伪代码示意):
long expireTime = Long.parseLong(request.getParameter("expire_time"));
long currentTime = System.currentTimeMillis() / 1000;
if (currentTime > expireTime) {
throw new IllegalRequestException("请求已过期,无效");
}
以下是 SignAuthInterceptor
类的代码解读,它实现了拦截 HTTP 请求并进行防重放攻击等验证的功能:
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();
}
}
这个拦截器主要做了以下几件事:
timestamp
、nonceStr
和 signature
参数,对时间戳进行时效性验证,防止请求时间过长(超过 60 秒)可能存在的重放攻击情况,不符合要求则抛出异常。nonceStr
是否已存在于 Redis 中,若存在则认为是非法请求(短时间内重放),同样抛出异常。nonceStr
参数存入 Redis 并设置 60 秒后自动删除,然后放行请求。RPC通信一般是使用TLS加密
以下是一段使用 Java 实现基于 TLS 进行加密传输的示例代码:
// 创建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());
// 设置其他请求参数、发送请求、处理响应等
这段代码的主要步骤及作用如下:
SSLContext.getInstance("TLS")
创建了一个 SSLContext
对象,指定使用 TLS 协议。KeyStore
中,需要提供正确的文件路径(如 "keystore.jks"
)以及对应的密码( "password"
),并利用 KeyManagerFactory
和 TrustManagerFactory
进行初始化配置,这一步是为后续 SSLContext
的初始化做准备。SSLContext
,使其具备加密通信所需的环境基础。HttpsURLConnection
对象与指定的 URL(如 "https://api.example.com/endpoint"
)建立连接,并通过 connection.setSSLSocketFactory(sslContext.getSocketFactory())
设置 TLS 的安全套接字工厂,使得后续通过该连接进行的数据传输都能基于 TLS 协议进行加密处理。之后还可以按常规流程进行请求参数设置、发送请求以及处理响应等操作。请注意,实际部署时,要将示例代码中的证书和私钥文件路径替换为真实有效的文件路径,并提供正确的密码,同时可能还需根据具体项目要求做更多的配置优化工作,确保三方接口的加密传输安全可靠。
需开发 API 密钥管理系统,可独立部署或集成于主应用。其核心功能为高效生成、精准管理 AK 和 SK,成为保障接口安全的核心枢纽。
UUID.randomUUID().toString()
即可快速生成 AK。对于成熟的 API 文档,发布后不宜随意修改接口,若需新增或修改接口,应加入版本控制。版本号可采用整数或浮点数类型,且一般在接口地址中体现,如 http://ip:port//v1/list
、http://ip:port//v2/list
,方便区分不同版本的接口,保障接口更新迭代过程中的兼容性与可管理性。
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
。key1value1key2value2…keyXvalueX
的方式拼接成一个字符串,上述例子中拼接后为 appIdzs001k1v1k2v2kXvXmethodcancelnonce1234567890timeStamp1612691221000
。miyao
,则新字符串变为 appIdzs001k1v1k2v2kXvXmethodcancelnonce1234567890timeStamp1612691221000miyao
。以下是对上述“Token相关内容”的总结:
在接口调用中,常用的请求方式有 GET 和 POST 两种。GET 请求会把参数直接暴露在浏览器 URL 里,并且对参数长度存在限制,从安全性角度考量相对薄弱。为增强接口的安全性,建议统一采用 POST 方式来发起请求,以此避免因参数暴露等问题带来的潜在安全风险。
记录接口请求日志有着重要意义,当系统出现异常请求时,通过查看这些日志,能够快速定位到异常请求发生的位置,进而方便排查问题产生的原因。例如,可以采用 AOP(面向切面编程)的方式来对接口请求进行全局处理,高效地记录下每个请求的相关关键信息,为后续的问题追溯与分析提供有力依据。
在接口调用环节,往往会涉及到像订单号这类敏感数据,出于数据安全和隐私保护的考量,通常需要对这类敏感数据进行脱敏处理。最常用的手段就是加密,其中安全性较高的 RSA 非对称加密方式应用较为广泛。非对称加密算法具备两个不同但又相互匹配的密钥,只有使用与之匹配的一对公钥和私钥,才能够完成对明文的加密和解密操作,以此确保敏感数据在传输和存储等过程中的安全性。实际上来说,使用https通信就没有问题了。
名词 | 解释 |
---|---|
APP_ID | 每个业务垂类会申请一个app_id,例如给一家公司可能分配两个app_id。一个是查询旅游信息的,一个是查询信用卡账单信息的 |
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。