马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
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企服之家,中国第一个企服评测及商务社交产业平台。 |