美文网首页Java技术升华分布式&高可用
常用系统间接口调用认证设计

常用系统间接口调用认证设计

作者: 逸如风飞 | 来源:发表于2019-08-02 10:11 被阅读58次

简介

本文实现接口加密签名及校验。
可适用于绝大多数系统间接口调用。

签名实现

请求签名实现过程

  1. 对请求参数名进行升序排序,排序之后生成请求参数字符串queryStr(拼接时要对参数值进行URLEncoder.encode编码,防止中文等问题),形如:参数a=xx&参数b=22。
  2. 系统间约定加密字符串key ,生成当前时间戳time(毫秒数),获取当前用户编号 uid(注意uid key time 这三个参数均不参与排序)
  3. 将步骤1中生成的queryStr 和 time 、key 、uid 进行拼接,形成待加密字符串str。形如 queryStr &time=xx&key=xxx&uid=xxx
  4. 对待加密字符串str进行MD5加密,并转化成大写,即生成签名字符串hash。
  5. 客户端将uid放到请求header中,将原始请求参数和time、hash一起作参数传递。

校验请求签名过程

  1. 从请求header中获取用户编号uid
  2. 从请求参数里获取签名字符串hash,以及请求时间time
  3. 再将请求参数(除去hash和time)进行一遍签名加密,生成出来正确的签名字符串hash2
  4. 比较hash和hash2即可

代码实现

public class FkSignUtil {
    public static final String UID = "uid";
    /**
     * 加密秘钥
     */
    private static final String KEY = "key可以自定义";
    /**
     * 日志对象
     */
    private static  Logger logger = LoggerFactory.getLogger(FkSignUtil.class);
    /**
     * 生成签名【注意发送请求时一定要带上time 和hash 这2个参数】
     *
     * 功能:将一个Map按照Key字母升序构成一个QueryString. 并且加入时间混淆的hash串
     * @param queryMap  query内容
     * @param time  加密时候,为当前时间;解密时,为从querystring得到的时间;
     * @param uid 表示当前用户id
     * @return
     */
    public static String createSign(Map<String, Object> queryMap,long time, String uid) {
        if(null == uid || "".equals(uid)){
            return null ;
        }
        String qs = sortQueryParamString(queryMap);
        if (qs == null) {
            return null;
        }
        String hash = MD5Util.MD5(String.format("%s&time=%d&key=%s&uid=%s", qs, time , KEY ,uid));
        hash = hash.toUpperCase();
        return hash;
//        String params = String.format("%s&time=%d&hash=%s", qs, time, hash);
//        return params;
    }

    /**
     * 对请求参数进行排序 
     * @param params 请求参数 。注意请求参数中不能包含uid、time【这2参数是排序之后拼接的】
     * @return
     */
    private static String  sortQueryParamString(Map<String,Object> params)  {
        List<String> listKeys = Lists.newArrayList( params.keySet());
        Collections.sort(listKeys);
        StringBuilder content = new StringBuilder();

        for(String param : listKeys){
            try {
                content.append(param).append("=").append(URLEncoder.encode(params.get(param).toString(),"UTF-8")).append("&");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        if(content.length()> 0){
            return content.substring(0 ,content.length() -1 );
        }
        return  content.toString();
    }

    /**
     * 解密判断是否签名正确
     * @param params 请求参数map【从request的参数中获取】
     * @param uid 表示当前用户id【从header中获取】
     * @return
     */
    public static boolean checkHashSign(Map<String, Object> params,String uid) {
        if(null == uid || "".equals(uid)){
            if (logger.isInfoEnabled()) {
                logger.info("checkHashSign ERROR: uid  is null.");
            }
            return false ;
        }
        if (!params.containsKey("hash") || !params.containsKey("time") ) {
            if (logger.isInfoEnabled()) {
                logger.info("checkHashSign ERROR: hash or  time  is null.");
            }
            return false;
        }
        String hash = (String) params.remove("hash");
        Long time =Long.parseLong((String) params.remove("time"));
        String signHash = createSign(params, time, uid);
        return hash.equals(signHash);
    }

public static void main(String[] args) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("zhangId", 12321);
        params.put("guanId", true);
        params.put("test", "这是11AVC");
        params.put("name", "是的商家");
        String uid = "asdsa";
        long time = System.currentTimeMillis();
        String hash = createSign(params, time, uid);
        logger.info("hash={} ,time={}", hash, time);
         
        params.put("hash", hash);
        params.put("time", time);
        boolean flag = checkHashSign(params, uid);
        logger.info("校验flag={}  ", flag);
    }

}

优化方案

可以考虑使用token缓存,免加密解密校验。
同时也可以校验time是否不是在有效期内,比如判断time是不是在当前时间的前2分钟之内,防止接口扩散重复调用。

https安全升级

利用阿里云申请 Symantec 免费版 SSL 证书。
在nginx上添加ssl证书到/etc/nginx/ssl.conf文件夹下面。
配置示例:


# 以下属性中以ssl开头的属性代表与证书配置有关,其他属性请根据自己的需要进行配置。
server {
    listen 443;
    server_name localhost;  # localhost修改为您证书绑定的域名。
    ssl on;   #设置为on启用SSL功能。
    root html;
    index index.html index.htm;
    ssl_certificate cert/domain name.pem;   #将domain name.pem替换成您证书的文件名。
    ssl_certificate_key cert/domain name.key;   #将domain name.key替换成您证书的密钥文件名。
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;  #使用此加密套件。
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;   #使用该协议进行配置。
    ssl_prefer_server_ciphers on;   
    location / {
        root html;   #站点目录。
        index index.html index.htm;   
    }
}

如无法访问请检查nginx所在机器的443端口是否打开

相关文章

  • 常用系统间接口调用认证设计

    简介 本文实现接口加密签名及校验。可适用于绝大多数系统间接口调用。 签名实现 请求签名实现过程 对请求参数名进行升...

  • 如何设计接口

    接口是用来系统间通信的定义,尤其异构系统之间的通信,接口的设计尤其重要。 一、 什么是好的接口? 对接口调用方来说...

  • SSO单点登录接口文档

    概述 本文是公司单点登录(SSO)产品的接口设计文档。 接口列表 票据验证接口 所属系统 SSO认证系统 接口地址...

  • 12章 系统调用(System Call) 与 API

    1 系统调用: 应用程序 (含 运行库 ) 与 OS Kernel 间 接口 2 系统调用 原理 2.1 中断 (...

  • Http请求实践

    后台开发时,经常需要进行系统对接,远程接口调用非常普遍,现总结一下针对http接口的远程调用 常用工具: Http...

  • 接口测试用例设计思路学习

    设计思路 1)优先级--针对所有接口1、暴露在外面的接口,因为通常该接口会给第三方调用;2、供系统内部调用的核心功...

  • JWT使用

    八幅漫画理解使用JSON Web Token设计单点登录系统 几种常用的认证机制 OAuth OAuth(开放授权...

  • ABAP发送http post请求

    在 SAP 常用的接口技术中,大多是外部系统主动请求,比如外部系统调用 RFC 函数,如果需要 SAP 侧主动推送...

  • Http连接池之:Timeout waiting for con

    背景 系统 A 调用系统 B 的接口,最近发现当 B 停机维护时,系统 A 在出发到调用系统 B 的接口的功能时会...

  • 4.系统调用

    系统调用 内核提供了用户进程和内核进行交互的一组接口,称这些接口为系统调用。通过接口的方式访问系统,保证系统的稳定...

网友评论

    本文标题:常用系统间接口调用认证设计

    本文链接:https://www.haomeiwen.com/subject/qypkdctx.html