首页后端开发其他后端知识Java的API如何编写,sign签名校验怎么使用

Java的API如何编写,sign签名校验怎么使用

时间2024-03-25 00:50:03发布访客分类其他后端知识浏览1549
导读:这篇文章分享给大家的内容是关于Java的API如何编写,sign签名校验怎么使用,本文介绍得很详细,内容有一定的参考价值,能帮助大家进一步学习和理解“Java的API如何编写,sign签名校验怎么使用”,有这方面学习需要的朋友可以看看,接下...
这篇文章分享给大家的内容是关于Java的API如何编写,sign签名校验怎么使用,本文介绍得很详细,内容有一定的参考价值,能帮助大家进一步学习和理解“Java的API如何编写,sign签名校验怎么使用”,有这方面学习需要的朋友可以看看,接下来就让小编带领大家一起来学习一下吧。

1. 前言

目的:为防止中间人攻击。

场景:

  • 项目内部前后端调用,这种场景只需要做普通参数的签名校验和过期请求校验,目的是为了防止攻击者劫持请求 url 后非法请求接口。
  • 开放平台向第三方应用提供能力,这种场景除了普通参数校验和请求过期校验外,还要考虑 3d 应用的授权机制,不被授权的应用就算传入了合法的参数也不能被允许请求成功。

2. 签名生成策略

接下来详述场景 2,其实场景 1 也包含在场景 2 内部。

1.举例请求 url:

http://api.abc.com/a-service/orders?orderType=1001& requestFrom=IOS& pageNum=2& pageSize=10

请求参数为:

参数名 位置 备注 举例
X-Access-Key header 客户端授权码,服务端提供,和 accessSecret 配对(场景 1 无此参数) app1
X-Access-Token header 当前登录用户 token d7b5808c3f443eb5a496225468c7e4a5
X-UTCTime header 当前发送请求时的时间 2022-02-16T09:12:43.083Z
X-Random header 请求随机数 341be97d9aff90c9978347f66f945b77
orderType query 订单类型 1001
requestFrom query 订单来源 IOS
pageNum query 分页参数 10
pageSize query 分页参数 2

2.设原始参数为 stringA,stringA 中添加 X-Access-Key、X-UTCTime、X-Random 固定参数,将 stringA 内非空参数值和 header 的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即 key1=value1& key2=value2…)拼接成字符串 stringB。

注意如下规则:

  • 参数名 ASCII 码从小到大排序(字典序);
  • 如果参数的值为空不参与签名;
  • 参数名区分大小写;
  • 验证调用返回或主动通知签名时,传送的 sign 参数不参与签名,将生成的签名与该 sign 值作校验。
// 最终拼接为stringB:
orderType=1001X-UTCTime=2022-02-16T09:12:43.083ZpageSize=10X-Access-Key=app1X-Access-Token=d7b5808c3f443eb5a496225468c7e4a5pageNum=2requestFrom=IOSX-Random=341be97d9aff90c9978347f66f945b77

3.在 stringB 最后拼接上 accessSecret (密钥) 得到 stringC 字符串,并对 stringC 进行 MD5 运算,得到 sign 值。

// 最后拼上accessSecret得到stringC:
orderType=1001X-UTCTime=2022-02-16T09:12:43.083ZpageSize=10X-Access-Key=app1X-Access-Token=d7b5808c3f443eb5a496225468c7e4a5pageNum=2requestFrom=IOSX-Random=341be97d9aff90c9978347f66f945b77&
    accessSecret=192006250b4c09247ec02edce69f6a2d
// md5加密得到最终签名结果sign:
sign=e1a4907ef03adee3fa8d395552814f4e

4.将原始的请求 url 拼接上 sign 形成最终的请求 url。

http://api.abc.com/a-service/orders?orderType=1001& requestFrom=IOS& pageNum=2& pageSize=10& sign=0f5a3cc534961d129a25d52d7ed8d003

5.最终请求 url 如下:

http://api.abc.com/a-service/orders?orderType=1001& requestFrom=IOS& pageNum=2& pageSize=10& sign=0f5a3cc534961d129a25d52d7ed8d003

参数名 位置 备注 举例
X-Access-Key header 客户端授权码,服务端提供,和 accessSecret 配对(场景 1 无此参数) app1
X-Access-Token header 当前登录用户 token d7b5808c3f443eb5a496225468c7e4a5
X-UTCTime header 当前发送请求时的时间 2022-02-16T09:12:43.083Z
X-Random header 请求随机数 341be97d9aff90c9978347f66f945b77
orderType query 订单类型 1001
requestFrom query 订单来源 IOS
pageNum query 分页参数 10
pageSize query 分页参数 2

6.服务端 gateway 同样做 sign 签名加密和校验,如果校验不通过则说明请求非法,直接拒绝,通过则下发到业务服务进行正常请求处理。

3. API 签名算法 Java 实现

public class SignUtil {
    

    /**
     * 生成签名
     *
     * @param accessSecret accessSecret
     * @param url          url
     * @param headers      headers
     * @param body         post的body体
     * @param T>
              body体泛型
     * @return sign
     */
    public static T>
     String sign(String accessSecret, String url, MapString, Object>
 headers, T body) throws IllegalAccessException {
    
        MapString, Object>
     signMap = new HashMap>
    ();

        if (headers != null) {
    
            signMap.putAll(headers);

        }
    
        MapString, Object>
     paramMap = getUrlParams(url);

        if (paramMap != null) {
    
            signMap.putAll(paramMap);

        }
    
        MapString, Object>
     bodyMap = getBodyParams(body);

        if (bodyMap != null) {
    
            signMap.putAll(bodyMap);

        }
    

        StringBuffer sb = new StringBuffer();
    
        signMap.forEach((k, v) ->
 {
    
            sb.append(k).append("=").append(v).append("&
    ");

        }
    );
    
        sb.append("accessSecret=").append(accessSecret);
    
        return stringToMD5(sb.toString());

    }
    

    private static MapString, Object>
 getUrlParams(String url) {

        if (StringUtils.isBlank(url) || !url.contains("?")) {
    
            return null;

        }
    
        MapString, Object>
     paramMap = new HashMap>
    ();
    
        String params = url.split("\\?")[1];
    
        for (String param : params.split("&
")) {
    
            String[] p = param.split("=");
    
            paramMap.put(p[0], p[1]);

        }
    
        return paramMap;

    }
    

    private static T>
     MapString, Object>
 getBodyParams(T body) throws IllegalAccessException {

        if (body == null) {
    
            return null;

        }
    
        MapString, Object>
     bodyMap = new HashMap>
    ();

        for (Field field : body.getClass().getDeclaredFields()) {
    
            field.setAccessible(true);
    
            bodyMap.put(field.getName(), field.get(body));

        }
    
        return bodyMap;

    }


    private static String stringToMD5(String plainText) {
    
        byte[] secretBytes = null;

        try {
    
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());

        }
 catch (NoSuchAlgorithmException e) {
    
            throw new RuntimeException("没有这个md5算法!");

        }
    

        return new BigInteger(1, secretBytes).toString(16);

    }

}

4. 测试一下

public class App {

    public static void main(String[] args) throws IllegalAccessException {
    
        String url = "http://api.abc.com/a-service/orders?orderType=1001&
    requestFrom=IOS&
    pageNum=2&
    pageSize=10";
    
        MapString, Object>
     headerMap = new HashMap>
    ();
    
        headerMap.put("X-Access-Key", "app1");
    
        headerMap.put("X-Access-Token", "d7b5808c3f443eb5a496225468c7e4a5");
    
        headerMap.put("X-UTCTime", generateDate());
    
        headerMap.put("X-Random", "341be97d9aff90c9978347f66f945b77");
    
        BodyVO body = new BodyVO(100000001L, "yangcan", new Date());
    

        String sign = SignUtil.sign("sdfsdfdsfdsfds", url, headerMap, body);
    
        System.out.println(sign);

    }


    /**
     * 获取当前时间的UTC格式
     */
    private static String generateDate() {
    
        Date now = new Date();
    
        DateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
    
        return format.format(now);

    }


    @Data
    @AllArgsConstructor
    public static class BodyVO {
    
        private Long ycId;
    
        private String ycName;
    
        private Date ycTime;

    }

}
    

输出:

sign = 4f52eb34b30129a8d511dc803044086b



通过以上内容的阐述,相信大家对“Java的API如何编写,sign签名校验怎么使用”已经有了进一步的了解,更多相关的问题,欢迎关注网络或到官网咨询客服。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Java的API如何编写,sign签名校验怎么使用
本文地址: https://pptw.com/jishu/652430.html
Vue中的computed属性如何使用,可以用做什么 vuex中mutations的应用有哪些,有何用

游客 回复需填写必要信息