代码实战:接口安全之API Key + Secret认证机制
1.实战配景如何包管服务端和客户端的HTTP接口安全?
即对外提供部门特定接口(比如:/**/noauth),第三方调用这些接口既不能走我们自己的权限认证(比如:header带着登录后的token),也不能随意让任何人随意调用。如何包管提供给第三方接口的安全性,特别如何校验第三方的身份?
2.焦点目标
身份验证 - 确保调用方是合法的第三方。
数据完整性 - 防止请求数据被篡改。
防重放攻击 - 防止请求被恶意重复使用。
访问控制 - 限制第三方只能访问特定接口。
3.实现方案 API Key + Secret认证机制
step1:天生签名
客户端使用appid、secret、时间戳(Timestamp)和随机字符串(Nonce)天生签名。签名算法使用HMAC-SHA256。【时间戳个随机数防重放攻击】
step2:发送请求
客户端将appId、Timestamp、Nonce和签名作为请求头发送到服务端。
step3:验证签名
服务端根据appId查找对应的secret,使用雷同的算法对请求参数进行签名,并与客户端提供的签名进行比对。
重点:要对外提供appid、secret,以及签名天生算法
// 其他方案也可,包罗HTTPS加密传输、IP白名单等
4. 具体实现
public class ApiAuthorizationUtils {
/**
* 客户端生成签名.
*
* @param appId 标记唯一第三方应用
* @param secret 密钥
* @param timestamp 时间戳
* @param nonce 随机数
* @return 验签
*/
public static String generateSignature(String appId, String secret, Long timestamp, String nonce) {
try {
// 构造待签名的字符串
Map<String, String> params = new TreeMap<>();
params.put("appid", appId);
params.put("timestamp", String.valueOf(timestamp));
params.put("nonce", nonce);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String signString = sb.substring(0, sb.length() - 1);
// 使用HMAC-SHA256进行签名
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] hash = sha256Hmac.doFinal(signString.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
throw new RuntimeException("Failed to generate signature", e);
}
}
/**
* 验证客户端签名.
*
* @param appId appId
* @param secret secret
* @param timestamp 时间戳
* @param nonce 随机数
* @param clientSignature 客户端验签
* @return 是否验证成功
*/
public static Boolean verifySignature(String appId, String secret, Long timestamp, String nonce, String clientSignature) {
try {
// 构造待签名的字符串
Map<String, String> params = new TreeMap<>();
params.put("appid", appId);
params.put("timestamp", String.valueOf(timestamp));
params.put("nonce", nonce);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String signString = sb.substring(0, sb.length() - 1);
// 使用HMAC-SHA256进行签名
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] hash = sha256Hmac.doFinal(signString.getBytes(StandardCharsets.UTF_8));
String serverSignature = Base64.getEncoder().encodeToString(hash);
return serverSignature.equals(clientSignature);
} catch (Exception e) {
throw new RuntimeException("Failed to verify signature", e);
}
}
/**
* 验证时间有效性(5分钟内有效).
*
* @param timestamp timestamp
* @return 是否有效
*/
public static boolean isTimestampValid(long timestamp) {
long currentTime = System.currentTimeMillis();
long timeDiff = Math.abs(currentTime - timestamp);
return timeDiff <= 5 * 60 * 1000;
}
}
Configuration
@Slf4j
public class AuthInterceptor implements WebMvcConfigurer {
/**
* 密钥,一般加密后存在数据库中,或者配置在配置文件.
*/
private static final String SECRET = "123456";
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
PrintWriter writer = response.getWriter();
// 从请求头中获取值
String appid = request.getHeader("appId");
Long timestamp = Long.valueOf(request.getHeader("timestamp"));
String nonce = request.getHeader("nonce");
String signature = request.getHeader("signature");
if (StrUtil.isBlank(appid) || StrUtil.isBlank(nonce) || StrUtil.isBlank(signature)) {
writer.append("permission denied, params is not valid");
return false;
}
if (!ApiAuthorizationUtils.isTimestampValid(timestamp)) {
writer.append("签名已过期");
return false;
}
// 服务端验证签名
boolean isValid = ApiAuthorizationUtils.verifySignature(appid, SECRET, timestamp, nonce, signature);
if (isValid) {
return true;
} else {
writer.append("权限认证失败");
return false;
}
}
}).addPathPatterns("/api/noauth/external/**"); // 第三方路由
}
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]