如何保证接口的安全性:加密协议(HTTPS)系统权限 接口签名


开发过程中经常会使用到接口,而接口的安全性是首要考虑的问题。防止其他非预期的任意调用或者是恶意攻击接口,对系统带来风险。

使用加密协议(HTTPS)来保证数据传输的安全性

关于HTTP和HTTPS :
超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
参考:HTTP与HTTPS的区别

使用系统身份认证/权限管理来限制接口的非法访问

对于身份认证失败或者不具有接口访问权限的接口访问,系统直接拒绝对应的请求。可自主实现也可使用第3方的权限框架,如Shrio等。

使用接口签名来验证请求的签名值sign是否和接口生成的签名值一致

大概意思:请求方和接口方根据特定同一规则生成签名值sign, 在请求时对比签名值,如果签名值一致则是合法的,否则视为非法—直接返回。

生成签名值方法:将数据经过md5运算生成字符串。

生成签名值例子 一

private String generateSignatureInfo(String checkNo,String flag,String timestamp) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		String appkey = "xyz"; // 与接口提供方(联通)的约定值
		String appSecret = "66666"; // 与接口提供方(联通)的约定值
		String info = checkNo + flag + timestamp + appkey + appSecret;
		MessageDigest  mdEnc = MessageDigest.getInstance("MD5"); // 使用md5签名算法
		mdEnc.update(info.getBytes("UTF-8"));
		// 在mdEnc.digest()的首位刚好等于0的时候,使用new BigInteger(1,mdEnc.digest())会导致0被省略,以致生成的签名信息错误, 这是个坑!!(所以不用这种方式)
//		String sign = new BigInteger(1,mdEnc.digest()).toString(16); // 生成签名信息
		
		/*
		 * 	使用以下方式生成签名信息:
		 * 	将每一个字节取出来,单独转成十六进制
		 */
		byte[] bytes = mdEnc.digest();
		Formatter formatter = new Formatter();              
        for (byte b : bytes) {          
            formatter.format("%02x", b);
        }

        String sign = formatter.toString();
        formatter.close();
		
		return sign;
	}

这里的appkeyappSecret的值是请求方和接口提供方共同约定的

生成签名值例子 二

参考:医网信电子签名API接口说明

sign值计算说明

这里的sign就是回调数据body中数据经过字典排序加上厂商appSecret后经过md5运算后生成的字符串。

主要分三步:

第一步,将回调数据中body里参数按照字母排序,保证固定顺序,因日后系统更新等原因,回调数据中body里的参数会有所增加,所以最好遍历整个body,确保将每一组key=value进行排序拼接,防止回调数据中参数增加导致验证sign错误;

第二步,遍历组装字符串之后拼接接入账号的appSecret当salt,产生如key1=value1&key1=value2#appSecret形式字符串;

第三步:对拼接字符串计算hash,目前统一采用md5算法,并转成16进制字符串。此16进制字符串就是回调数据head中的sign值。

将本地计算sign值同回调中的sign对比,比对一致说明数据来源可靠且未变更篡改。

代码示例:

public static void main(String[] args) {
//        回调数据json中取body,
        String jsonStr="{\"process\":\"2\",\"stamp\":\"KonWykAGc9t9rgtd8E8AJJZD+OD23kaVBgWL6MHJM3oSSrOje2ynpBjP+JDRPPje0iuyDtgSIP9hKUk6HsgkBpxmH4D9KlFdepngFZPYkx4yRl2h5zph2YnayfJ1RHHOrfm/ykwjRgLV4UUKEA4LZ70xqGSyT0zkQE11:55:33\",\"phoneNum\":\"13333333333\",\"stampStatus\":\"10\",\"openId\":\"bf61fe5f9807ed3bqe5f4w5b33333ca\"}";
        Map<String, String> bodyMap = new HashMap<String, String>();
        JSONObject jsonObj = JSON.parseObject(jsonStr);
//        遍历json,将key和value存入map
        for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
            bodyMap.put(entry.getKey(), entry.getValue().toString());
        }
        String sign=buildThirdSign(bodyMap, "111111");
                System.out.println(sign);
    }
    public static String buildThirdSign(Map<String, String> mapBody, String clientSecret) {
        try {
            StringBuffer paramStr = new StringBuffer();
            String resultStr = "";
            // 字母排序,使用key=value,并用&分割的形式拼接字典排序字符串
            if (mapBody != null && mapBody.size() > 0) {
                List<String> paramsKeyList = new ArrayList<String>(mapBody.keySet());
                Collections.sort(paramsKeyList);
                for (String key : paramsKeyList) {
                    String value = String.valueOf(mapBody.get(key));
                    if (value != null && !"".equals(value)) {
                        paramStr.append("&" + key);
                        paramStr.append("=" + value);
                    }
                }
            }
            // 去掉最左侧第一个&符号,并用#把clientSecret与字典排序字符串进行拼接
            String signSourceStr = paramStr.subString(1) + "#" + clientSecret;
            // 计算字符串md5 16进制字符串,此值就是回调数据中的sign
            resultStr = getMd5(signSourceStr);
            return resultStr;
        } catch (Exception e) {

        }
        return clientSecret;
    }
    public static String getMd5(String s) {
        char[] hexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
                'f' };
        try {
            byte[] e = s.getBytes("UTF-8");
            MessageDigest mdTemp = MessageDigest.getInstance("md5");
            mdTemp.update(e);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;

            for (int i = 0; i < j; ++i) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 15];
                str[k++] = hexDigits[byte0 & 15];
            }

            return new String(str);
        } catch (Exception arg9) {
            return null;
        }
    }

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值